Brief reads every fact from the Implementation aggregate; project config binds at open (was: brief ignores target) #534
Labels
No labels
P0
P1
P2
P3
bug
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#534
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Brief reads every fact from the Implementation aggregate; project config binds at open
Reframed 2026-09-08 from "programmer brief ignores implementation target". The target drop in
BriefFacts#projectis one symptom of a class-level rule violation: the brief queries tables, loads yaml, and shells out to git on its own instead of asking the models that own those facts. Every fact a brief needs already exists before the brief is built. The brief's only job is to read and decorate them.Symptom (original report)
Live run of
/os-sdlc:implement #525(session f3ac4424, 2026-09-08). Programmer SubagentStart hook raisedTargetSelectionErroratplugins/os-sdlc/lib/os_sdlc/runner/brief_facts.rb:134(Project.load(@project_root)with no target in a multi-target project). No dispatch row was written; the stop settled against a stale dispatch from 2026-09-04. Hot-patched in-run with sed, restored after.Root cause
BriefFacts#projectcallsProject.loadwithout the target the implementation row already holds (implementations.target, ADR-0139). Written ind4c0d4cafter multi-target support existed; every test on this path uses a flat single-target fixture, so the single-target fast path hid it.Project.load(root)calls exist in the runner path:brief_facts.rb:133,autocorrect_prepass.rb:95,tea_tracker.rb:146,scope_policy.rb:15. Any of them fails the same way.Project.load(dir, target: nil)has an optional keyword with a silent fallback, which invites the omission.Design (decided with the user, 2026-09-08)
OpenImplementationparses.sdlc/project.yamlonce withProject.load(root, target:)(keyword becomes required) and stores the resolved attribute set as JSON in a newimplementations.project_configtext column. Relative paths only; root is never stored (ADR-0055) and stays derived from cwd.Implementation#projectrebuilds the PORO viaProject.from_json(project_config). Config is frozen for the life of the implementation, same rule as the map (ADR-0160). Schema bump to 21 (ADR-0129 wipe-and-recreate).Project.loadoutside implementation-open. The four bare call sites readimplementation.project. The required keyword makes a bare call a load-time error.BriefFacts.new(implementation). Droptracker:(ticket body is on the row since ADR-0146; the fallback is a fossil) andproject_root:/db:(models know their datasets).ticket_brief->implementation.ticket_bodygate_results->implementation.latest_dispatch&.step_summaries || {}interpreted_behavior/probe_evidence/seam->Handoff.latest(implementation, from: agent_type)&.contentlint_offense_history->StepResult.lint_failures_for(implementation)formatted by an extractedLintOffenseLine(the nestedLintFailureRowclass moves out)test_changes->TestDiff.new(implementation.project).to_s, the one live message (git diff), in its own named classMAX_GATE_OUTPUT_CHARSstays in the brief: prompt budget is a brief concern.write_multi_target_project_yaml) as well as the flat one. Add a test that a SubagentStop for implementation B cannot settle against implementation A's pending dispatch (the stale-dispatch half of the original symptom).Out of scope
ADRs
/os-adr:findon 2026-09-08.Implementation.open/OpenImplementation).Sequencing
First phase 5 slice (os-sdlc architecture refactor plan). Land after #535.
Runner programmer brief ignores implementation target in multi-target projectto Brief reads every fact from the Implementation aggregate; project config binds at open (was: brief ignores target)Reframed 2026-09-08 after auditing session f3ac4424: symptom is one of four bare Project.load calls; design now binds config at open and makes BriefFacts read only through the aggregate. First phase 5 slice, lands after #535.
Design point 5 (added 2026-09-08, user decision): handoff facts resolve by convention, not by a translation table. Delete HANDOFF_FACT_AGENT_TYPES. In the map, an enrich: entry is either a named fact (ticket_brief, gate_results, lint_offense_history, test_changes) or an agent type; an agent type resolves to Handoff.latest(implementation, from: name)&.content. Map edit: interpreted_behavior -> change-interpreter, probe_evidence -> code-probe, seam -> seam-designer in poodr-implementation.yaml. Map::Validator refuses any enrich name that is neither a named fact nor a member of AgentType::ALL. Brief section headings follow the new names; any agent prompt that cites the old labels gets the same rename.
Child issues
Decomposition of #534 into six ordered children (A→F):
Close-out: parent closes when all six children are merged and the multi-target live run passes.
Resolution
Done: Project config binds at open and the brief reads every fact from the Implementation aggregate (ADR-0169). Children A-F all merged: #537 Project.load requires target: and JSON round-trips without root; #538 schema 21 stores project_config at open, Implementation#project rebuilds it, stale-dispatch guard; #539 no Project.load in runner/ outside open (Project::Source role, guard test); #540 fact owners on the models; #541 BriefFacts asks by name and drops the tracker fallback; #542 handoff facts by convention with an enrich validator and the alias table deleted.
Evidence: All six children closed 2026-09-09 with merges on main: 1f8bb6c/1e7b6f4 (#537), 164227d/2d7f1c9 (#538), afb5440/b9eb84d (#539), acc1862..10a055a (#540), 1a7189c..6ed104f (#541), 8a05410..c265f9f (#542). Multi-target live run: every child from #537 on was driven by /os-sdlc:implement on this multi-target project with --target os-sdlc; from #539 on the runs used the merged bound-project brief with no hot-patch, and #540, #541 and #542 ran on the schema-21 runner end to end (implementation_complete on #540, #541, #542). Suite 1121 runs green on main
c265f9f.Follow-ups: #544 (test-side conflict routing), #545 (Project.load sites outside runner/), #546 (verifier early-complete and dispatch cap) remain open; none new