Round 2 candidate 7a585aa9606863a0d44a26094b585160914e3d8fd5f13436b56470cd69e6d867,
packet manifest 4292da90162c801a9c56716a0539f8fa072e67e93e4f4d9a0f1e107b84b652ea,
base 02ee7f640. Filbert approved round 2 in comment 28707. Committed by Sage
for Rocko, who has no git access; secrets grep clean, no binaries (one empty file).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
3.4 KiB
Row 147 layout contract, version 1
Initial design base8686c7e84; candidate rebased ontofba879f6a; scope additions sage-0400 / brief10d4b526a. No live steps.
The separate local repository resolves from business.projects[project].repo, or dataRoot/projects/business.id/project. root remains the code checkout; the two paths must not overlap. IDs and normalized absolute paths are checked.
Layout:
- project.json: layoutVersion=1, business, project, name, createdAt (canonical UTC).
- spec/north-star.md: the human's Markdown north star.
- docs/*.md (nested Markdown is accepted by check).
- requirements/ID.json: recordVersion=1, id, title, text, status (draft, accepted, complete).
- decisions/ID.json: recordVersion=1, id, question, options [{key,text}], recommendation, status (open, accepted, rejected), choice (null while open). Option keys are distinct, recommendation/closed choice name an option.
- templates/{project,requirement,decision}.json and {document,north-star}.md. These are data/templates, never schemas; every JSON template validates against the package-owned schema. Empty requirement/decision directories use an empty .gitkeep, checked as a layout marker, so the layout survives checkout.
The package owns one closed JSON schema for project, requirement and decision. A small fail-closed validator implements only keywords those schemas use, and refuses unsupported schema keywords instead of ignoring a future constraint. No project-local schema is loaded. Markdown must be bounded UTF-8 without NULs. Check reports all discovered layout/record/name problems, rather than only the first. Unknown top-level paths, links and non-regular files refuse. .git is validated separately and never treated as a content directory.
Public APIs (packages/project/src/index.mjs):
- projectRepo({business,project,dataRoot}) -> absolute repository path.
- initProject(context) -> {repo,commit}; first commit is authored by business.human.
- checkProject(context) -> {ok,repo,problems}; read-only, no bus/network.
- validateRecord(kind,value) -> problem strings.
- writeRecord({...context,path,record,author:{definition,instance}}) -> {repo,path,commit}. path is docs/NAME.md, requirements/ID.json or decisions/ID.json. Names match JSON IDs. spec/ is refused for every agent actor. The trusted caller supplies actor metadata; row138 binds it to the authenticated role. This package only validates the actor's shape, not a new identity/proof system.
Writer requires the entire project to pass check, a clean worktree/index and no remotes. It uses a per-repository exclusive writer lock, sanitized local git (no global/system config, hooks, filters, fsmonitor or transports), validates before writing, and commits only the requested record as definition.instance. It won't absorb pending human edits. Commit failure rolls back only its own uncommitted file/index when HEAD is unchanged; changed/uncertain HEAD refuses without destructive repair. Init refuses a non-empty directory and undeclared project. No remote is created; no git push/fetch exists in this package.
CLI uses the existing system/business loaders, then these APIs. Both commands are local: mosaic project init BUSINESS PROJECT and mosaic project check BUSINESS PROJECT. scripts/mosaic dispatch and stack-copy's package inclusion are the only scope additions. No broker/harness path, grants, spec commit citations, Console editor or schema change belongs to this row.