@antoneeo/agentic-sdlc-skill 1.50.0 → 1.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,70 @@
2
2
 
3
3
  Tutte le modifiche significative a questa skill saranno documentate in questo file.
4
4
 
5
+ ## [1.52.0] - 2026-11-01
6
+
7
+ ### What's new
8
+
9
+ **A session that writes only documents no longer costs you a branch.** When you run several sessions with a coordinator, a session working only on design documents can work in the folder the others already use, instead of a worktree of its own. It writes only its own unit's files, runs no git command that changes the folder, and leaves index regeneration and its closure steps to the coordinator, so you no longer merge one branch per documents-only unit. A session that writes code still holds the code lease, or works in its own worktree.
10
+
11
+ **Every file several sessions touch has one writer.** Each session adds only its own rows to the review log. Translation catalogs count as code, so in the shared folder only the session holding the code lease writes them. devPNT's store goes in the Shared section of a commit map.
12
+
13
+ **The coordinator survives compaction and changes only when you say so.** After its context is compacted, the coordinator rereads the leases and the shared request record before acting, instead of trusting its summary. A session it starts does not become coordinator. To change coordinator, name the new session: it records your words, tells the previous coordinator, which stops, and sends you anything written in between.
14
+
15
+ **Branches that add review rows merge without a conflict.** When two branches both add rows to the review log and your project has no merge rule for that file, merging them stops on a conflict you resolve by hand. The check the agent runs when it closes a piece of work now notices the missing rule and prints the exact line to add to `.gitattributes`, so the agent sees what to fix while it works, without anyone reading release notes. Nothing changes at session start, and the note never makes a check fail.
16
+
17
+ #### What to do
18
+
19
+ **1. Update (everyone).** The changes arrive only with the update.
20
+ 1. Run `npm install -g @antoneeo/agentic-sdlc-skill@latest`, then `agentic-sdlc-install-skill`.
21
+ 2. Close and reopen your agent sessions: the new version loads only at startup.
22
+
23
+ To check: `npm ls -g @antoneeo/agentic-sdlc-skill` shows 1.52.0.
24
+
25
+ ### Changed
26
+ - `coordination.md` (F-089): the checkout a runtime mounts is the one exception to «own worktree» under coordination — the code-writer lease holder, a documents-only part (its own unit's files under the docs root and its own review-log rows; no git command that changes the checkout, no `index` or `mark`) and the coordinator, which alone runs `index`, `review-map` and `mark` there; the switch/stash/reset bans hold. «Code» defined (translation catalogs included). §1 retitled «Shared files: one writer each»: review log, translation catalogs, an external tool's store tracked in git. Who coordinates: the named conversation keeps the role across compaction and rereads `leases.md` and the shared record; a session another starts never inherits it; a new owner entry changes it, with a handover window up to the previous coordinator's answer. `leases.md` opens with a `coordinator` line. Ruling r42.
27
+ - `SKILL.md` Phase 4: the shared checkout, not the code-writer lease alone, is the exception to the own worktree; the bans hold there and only its coordinator runs `index`.
28
+ - `templates.md`: `leases.md` opening line; a commit-map Shared example for devPNT's store.
29
+ - The shared validator (F-090): `check` prints a `[note]` when the review log (`<docs>/audit/reviews/REVIEW_LOG.md`) has no `merge` attribute, naming the line to add to `.gitattributes` at the repository root (`git check-attr`, with personal and system attribute files ignored; a per-clone `.git/info/attributes` still counts). A deliberate non-union driver is respected. The exit code is unchanged, and session orientation still runs no git.
30
+ - `ENFORCEMENT.md`: «A review log git would not merge cleanly -- `check` names it» (F-090).
31
+ - `templates.md`: the `.gitattributes` stanza's note says `check` names a missing stanza; `coordination.md` §1: the coordinator adds it (F-090).
32
+
33
+ ## [1.51.0] - 2026-10-31
34
+
35
+ ### What's new
36
+
37
+ **Several sessions can work on one repository, one of them coordinating.** When you run several agent sessions on the same project at once and tell one of them that it coordinates the others, they follow shared rules. Each request record has a single writer, so its entries are never numbered twice. A resource only one session can use at a time, such as the development container or the test database, is taken and released through the coordinator, so one session no longer restarts what another is running against. An authorization you gave one session is not passed to another by message. A session that stops reports what it did, and the coordinator checks it before telling you the work is done. Before a commit that carries several sessions' work, the coordinator checks that every changed file is assigned to the right unit. If you name no coordinator, nothing changes.
38
+
39
+ **Decisions taken on your behalf are labelled.** In the request record, a decision a session took under a delegation you gave it, and words you gave one session for work another session records, now carry their own heading. They are never read later as something you said yourself, and a delegated decision never overrides one of yours: a choice that would change it comes back to you as a question.
40
+
41
+ **The reminder of what must not break also covers files written from the shell.** From 1.49.0 the agent is reminded of a unit's constraints before it edits a file; a file it writes through a shell command never triggered that reminder, so the agent now looks the constraints up itself.
42
+
43
+ #### What to do
44
+
45
+ **1. Update (everyone).** The changes arrive only with the update.
46
+ 1. Run `npm install -g @antoneeo/agentic-sdlc-skill@latest`, then `agentic-sdlc-install-skill`.
47
+ 2. Close and reopen your agent sessions: the new version loads only at startup.
48
+
49
+ To check: `npm ls -g @antoneeo/agentic-sdlc-skill` shows 1.51.0.
50
+
51
+ **2. Coordinating several sessions (only if you run them in parallel).**
52
+ - **This concerns you if** you run more than one agent session on the same repository at the same time.
53
+ - **Why:** without a coordinator, sessions can write the same record at once, restart a container another session is using, or leave one commit nobody can split by unit.
54
+ - **Steps:** tell the session you want as coordinator, in your own words, that it coordinates the others; it records your words, and the rules apply from then on. Each other session asks it before using a shared resource. Before committing several sessions' work together, it writes the commit map and runs `sdlc_check.py commit-map <map file>`.
55
+ - **To check:** when a session takes a shared resource, `ai_docs/audit/leases.md` gains a `take` line naming it.
56
+
57
+ | New file | Skill | Who reads it |
58
+ |---|---|---|
59
+ | `coordination.md` | agentic-sdlc | the agent, only while several sessions share a repository |
60
+
61
+ ### Added
62
+ - `coordination.md` (F-088): rules for several peer sessions on one repository once the owner names a coordinating session in a request-record entry — one writer per record (a part's owner words for the shared unit go through its own record and the coordinator), leases on exclusive resources in `audit/leases.md` (`take`/`release` lines, written by the coordinator only; a permission given before the naming exempts no session; the code-writer lease passes only once the holder's code is committed or set aside with the owner's yes), decisions under delegation recorded as labelled entries that never reach an earlier owner decision, authorizations valid only in the session they were given to, a fixed closure message per part (and, for a part that stopped without one, the coordinator reads its HANDOFF and working tree and tells the owner what it can and cannot tell), and the commit map. Rulings r40, r41.
63
+ - `sdlc_check.py commit-map MAP`: checks a commit map against the working tree against HEAD. Errors: a generated file under a unit, a changed authored file under Regenerated, a changed HANDOFF or request record in no section, a file under two units outside Shared, a file both under a unit and in Left out. Warnings: a change in no section, a listed file with no change. Exit 1 on any error.
64
+
65
+ ### Changed
66
+ - Request record (`templates.md`): labelled entries `decided under delegation R<m>` and `received for <unit>`; `review.md` and `elicitation.md` treat them as the deciding session's choice or relayed words, never an owner statement (all four lenses).
67
+ - `SKILL.md` Phase 4: constraint recall asks the agent to run `constraints --file` itself for a file written from the shell, which the recall hook never sees; `ENFORCEMENT.md` §3 names the residual. Phase 4 also names the code-writer lease as the one exception to "own worktree" under coordination.
68
+
5
69
  ## [1.50.0] - 2026-10-31
6
70
 
7
71
  ### What's new
package/README.md CHANGED
@@ -4,17 +4,15 @@
4
4
 
5
5
  <!-- whats-new:begin -->
6
6
  <!-- generated by scripts/whats_new.js from CHANGELOG.md: edit the CHANGELOG, then run node scripts/whats_new.js --write -->
7
- ## What's new in 1.50.0
7
+ ## What's new in 1.52.0
8
8
 
9
- **You approve the design before any code is written.** On significant work, once the independent design review passes, the agent stops and shows you a short summary of the design: what the change does, for whom and through which surfaces, which files it touches, the risks and how they are handled, and what the review found. The work starts only after your yes, which the agent records in the unit's request record. A "go ahead" given before you saw the design does not count, and in a session with nobody to answer the unit waits for you. A design that is right but not what you meant is cheapest to fix before the code exists. If you work with devPNT, your acceptance of the design proposal there is this approval, and nothing else is asked.
9
+ **A session that writes only documents no longer costs you a branch.** When you run several sessions with a coordinator, a session working only on design documents can work in the folder the others already use, instead of a worktree of its own. It writes only its own unit's files, runs no git command that changes the folder, and leaves index regeneration and its closure steps to the coordinator, so you no longer merge one branch per documents-only unit. A session that writes code still holds the code lease, or works in its own worktree.
10
10
 
11
- **The npm page tells you what is new.** This section, at the top of the package page, now summarizes each version for the people who install it.
11
+ **Every file several sessions touch has one writer.** Each session adds only its own rows to the review log. Translation catalogs count as code, so in the shared folder only the session holding the code lease writes them. devPNT's store goes in the Shared section of a commit map.
12
12
 
13
- **From 1.49.0, published without notes:**
14
- - **Fewer regressions on significant changes.** When an analysis states what must not break, the agent is reminded of it just before it edits that file, not only at the start of the work, and the reviewer has the list to check. It is a reminder and never blocks the work; for now it works on Claude Code.
15
- - **The process checks can no longer be bypassed by accident.** Quoting a work level inside a sentence no longer counts as declaring it, and protected parts of the project stay protected when the agent changes folder.
16
- - **You know what the project memory has not read yet.** The agent can tell how much of the project it has never examined (automatically in projects with a corpus, on request otherwise).
17
- - **Defects found after the merge can be recorded** without distorting the review statistics.
13
+ **The coordinator survives compaction and changes only when you say so.** After its context is compacted, the coordinator rereads the leases and the shared request record before acting, instead of trusting its summary. A session it starts does not become coordinator. To change coordinator, name the new session: it records your words, tells the previous coordinator, which stops, and sends you anything written in between.
14
+
15
+ **Branches that add review rows merge without a conflict.** When two branches both add rows to the review log and your project has no merge rule for that file, merging them stops on a conflict you resolve by hand. The check the agent runs when it closes a piece of work now notices the missing rule and prints the exact line to add to `.gitattributes`, so the agent sees what to fix while it works, without anyone reading release notes. Nothing changes at session start, and the note never makes a check fail.
18
16
 
19
17
  #### What to do
20
18
 
@@ -22,20 +20,7 @@
22
20
  1. Run `npm install -g @antoneeo/agentic-sdlc-skill@latest`, then `agentic-sdlc-install-skill`.
23
21
  2. Close and reopen your agent sessions: the new version loads only at startup.
24
22
 
25
- To check: `npm ls -g @antoneeo/agentic-sdlc-skill` shows 1.50.0.
26
-
27
- If you do not want the constraint reminder (Claude Code only, from 1.49.0): remove the entry that contains `constraints --hook` from `~/.claude/settings.json`. Later updates will not put it back.
28
-
29
- **2. Approving the design (significant units).**
30
- - **This concerns you if** you use the skill for significant (L3) work. Nothing to configure: after the design review passes, the agent asks whether to proceed. Answer yes to start the code, or say what to change; the design is then revised and, where the change touches what the review judged, reviewed again before you are asked again.
31
- - **A unit already in progress.** Units started before 2026-10-31 are not affected. If a unit started on or after 2026-10-31 with an earlier version is already in progress or finished, `sdlc_check.py validate` shows a `[note]` saying it has `no design approval in its request record`. Why: the earlier version never asked you. To clear it, tell the agent that you approve the design as it was implemented; it records your words under a label that begins `design approved` (for example `design approved after implementation`). The note never fails a check, so you can also leave it.
32
- - **To check:** on your next significant unit, the agent stops after the design review and asks you before any code starts.
33
-
34
- **3. Protected folders in a repository with several `ai_docs/` (only if you use them; from 1.49.0).**
35
- - **This concerns you if** your agent settings contain `gate --hook --protected` and the repository has more than one `ai_docs/` folder, for example a package with its own.
36
- - **Why:** protected paths are now read from the `ai_docs/` nearest to the edited file. A path written from the repository root, such as `packages/api/src/auth`, no longer matches the files inside `packages/api`, so the agent could change them without an analysis.
37
- - **Steps:** rewrite those paths from the package: `packages/api/src/auth` becomes `src/auth`. Note that `src/auth` then protects the folder with that name in every project that has its own `ai_docs/`.
38
- - **To check:** in a session with no open analysis, ask the agent for a small change to a protected file. It must be refused.
23
+ To check: `npm ls -g @antoneeo/agentic-sdlc-skill` shows 1.52.0.
39
24
 
40
25
  Full history: [CHANGELOG](https://github.com/Antoneeo/agentic-sdlc-skill/blob/main/CHANGELOG.md)
41
26
  <!-- whats-new:end -->
@@ -57,9 +42,9 @@ Full history: [CHANGELOG](https://github.com/Antoneeo/agentic-sdlc-skill/blob/ma
57
42
  - **devPNT symbiosis**: when devPNT is available, Master Plan, Action Plan, M-VISION, and governed artifacts become the authoritative planning layer, and every governed artifact passes an independent agent review before it is proposed; devPNT itself performs no review — it is where you review and approve.
58
43
  - **Independent review, twice**: the design is reviewed before it is implemented and the diff before the work is declared done — by somebody other than its author, who receives the whole review mandate in a fixed block copied verbatim, never a paraphrase of it. Three rungs of independence (fresh-context subagent > one-shot run > a declared self-pass, legal only when no higher rung is usable: absent, or permission-gated and declined — a gated rung is asked about, never silently skipped, and the log says which), capped at 3 rounds, one log line per review, and a PASS is invalid on "found nothing" — it must state where each constraint is satisfied. After the design review passes, the owner approves the design before any code is written.
59
44
  - **Question discipline**: a real doubt is asked when it emerges, before its answer is written into any document — grouped, with the pros and cons of each option. A choice whose pros clearly win is taken without asking, and the weighing is written next to it; a fact is settled by evidence or asked, never weighed. A question is legal only when the agent searched first, names the search with its result, and names the decision it unblocks — never a stream of "shall I proceed?".
60
- - **Installed support files**: Claude, Codex, Gemini, and Google Antigravity receive the full skill folder, including `templates.md`, `architect.md`, `guides.md`, `vision.md`, `tdd.md`, `debugging.md`, `elicitation.md`, `review.md`, `dispatch.md`, `routing.md`, `hybrid.md`, `ENFORCEMENT.md`, `memory.md`, and the validator's three files, `scripts/sdlc_check.py` + `scripts/sdlc_core.py` + `scripts/knowledge.py` — plus the docs site generator `scripts/docs_site.py` and its page `docs_site/`, and the `LICENSE` and `NOTICE` it ships under.
45
+ - **Installed support files**: Claude, Codex, Gemini, and Google Antigravity receive the full skill folder, including `templates.md`, `architect.md`, `guides.md`, `vision.md`, `tdd.md`, `debugging.md`, `elicitation.md`, `review.md`, `dispatch.md`, `routing.md`, `coordination.md`, `hybrid.md`, `ENFORCEMENT.md`, `memory.md`, and the validator's three files, `scripts/sdlc_check.py` + `scripts/sdlc_core.py` + `scripts/knowledge.py` — plus the docs site generator `scripts/docs_site.py` and its page `docs_site/`, and the `LICENSE` and `NOTICE` it ships under.
61
46
  - **Docs site** (`scripts/docs_site.py --root <project> [--open] [--zip <file>]`): a static, offline, read-only website of what the agent reads in the project — reading guide, Vision, rules, documents, open work with each next step, analyses and, with a knowledge base, the topic map, each fact with its exact source passage, and the source documents. Written to `.docs-site/` (added to `.gitignore` on the first run), outside `ai_docs/`; nothing is ranked or recommended.
62
- - **Mechanical checks**: optional validator for document structure, generated feature history, stale audit areas, and protected-path gates — `check`, `validate`, `index`, `stale`, `mark`, `benefit`, `gate`, `plan`, `orient`, `migrate`, `review-map` (code lens: classes the files of a change for the merge review brief), `constraints` (code lens: a unit's constraints, or those binding a file). `benefit` reports what the review gates have already caught, beside what the doctrine costs to read — it always exits 0 and carries no verdict, because a measurement that can fail a build becomes a target.
47
+ - **Mechanical checks**: optional validator for document structure, generated feature history, stale audit areas, and protected-path gates — `check`, `validate`, `index`, `stale`, `mark`, `benefit`, `gate`, `plan`, `orient`, `migrate`, `review-map` (code lens: classes the files of a change for the merge review brief), `commit-map` (code lens: checks the map of one commit carrying several parallel sessions' work), `constraints` (code lens: a unit's constraints, or those binding a file). `benefit` reports what the review gates have already caught, beside what the doctrine costs to read — it always exits 0 and carries no verdict, because a measurement that can fail a build becomes a target.
63
48
 
64
49
  ## Shared project memory
65
50
 
@@ -141,6 +126,7 @@ skills/agentic-sdlc-skill/
141
126
  ├── review.md
142
127
  ├── dispatch.md
143
128
  ├── routing.md
129
+ ├── coordination.md
144
130
  ├── hybrid.md
145
131
  ├── memory.md
146
132
  ├── ENFORCEMENT.md
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-sdlc-skill",
3
- "version": "1.50.0",
3
+ "version": "1.52.0",
4
4
  "description": "Documentation-First SDLC protocol with triage, Vision governance and optional devPNT integration.",
5
5
  "author": "Antonio Pinto (https://github.com/Antoneeo)"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antoneeo/agentic-sdlc-skill",
3
- "version": "1.50.0",
3
+ "version": "1.52.0",
4
4
  "description": "Documentation-First SDLC protocol for Claude Code, Gemini CLI, Google Antigravity and Codex with risk triage, Vision governance, installed support files and optional devPNT integration.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -37,6 +37,7 @@
37
37
  "skills/agentic-sdlc-skill/dispatch.md",
38
38
  "skills/agentic-sdlc-skill/hybrid.md",
39
39
  "skills/agentic-sdlc-skill/routing.md",
40
+ "skills/agentic-sdlc-skill/coordination.md",
40
41
  "skills/agentic-sdlc-skill/ENFORCEMENT.md",
41
42
  "skills/agentic-sdlc-skill/scripts/sdlc_check.py",
42
43
  "skills/agentic-sdlc-skill/scripts/sdlc_core.py",
package/scripts/init.js CHANGED
@@ -203,16 +203,16 @@ in the skill's \`routing.md\`: it decides which lens's method and validation rul
203
203
  that unit. L1 never reaches it. Never refer to a document whose meaning differs by lens
204
204
  ("threat model", "vision", \`principles.md\`, \`handoff.md\`) by its bare name.
205
205
 
206
- Select the owning lens by the unit's purpose, then apply only its risk scale.
207
- The project's always-on pointer stays intact: consult the selected skill rather than
208
- merging multiple mandatory ladders. All lenses already carry shared project memory
209
- (\`memory.md\`); another installation is not needed for cross-domain recall.
206
+ Select the owning lens by the unit's purpose, then apply only its risk scale.
207
+ The project's always-on pointer stays intact: consult the selected skill rather than
208
+ merging multiple mandatory ladders. All lenses already carry shared project memory
209
+ (\`memory.md\`); another installation is not needed for cross-domain recall.
210
210
  This file is NOT auto-loaded by any client: it is a note for you, deliberately additive.
211
- Delete this note once the routing pointer is integrated.
211
+ Delete this note once the routing pointer is integrated.
212
212
  `, 'multi-lens routing note');
213
213
  if (wrote && protocolPreexisting) {
214
214
  console.log(' ⚠️ A protocol pointer already existed and was NOT overwritten.');
215
- console.log(' Integrate the routing pointer from AGENTIC_MULTI_LENS.md; keep one risk scale per unit.');
215
+ console.log(' Integrate the routing pointer from AGENTIC_MULTI_LENS.md; keep one risk scale per unit.');
216
216
  }
217
217
  }
218
218
 
@@ -130,7 +130,8 @@ is the one holding the edited file, never the hook's working directory.
130
130
  It informs and never decides: its JSON carries `additionalContext` only, never a
131
131
  `permissionDecision`, so the user's permission flow is the one without it. It is silent
132
132
  everywhere else and whenever it cannot tell (subagent, oversized or unreadable
133
- transcript, any error), and always exits 0.
133
+ transcript, any error), and always exits 0. A file written from the shell never reaches
134
+ it: for that file the agent runs `constraints --file` itself (SKILL.md Phase 4).
134
135
 
135
136
  Cost: up to 15 lines of context on a bound edit at L3 (owner-accepted,
136
137
  `REQUEST_working_model.md` R12 (b)), zero tokens at any other level; and about 0.25 s
@@ -182,6 +183,16 @@ not see them, because the wired hook legitimately lives in the git-ignored
182
183
  mechanism (Gemini today) keeps the note -- read it as the manual-reads reminder; a
183
184
  `.codex/hooks.json` wired in a shape other than the documented one keeps it too.
184
185
 
186
+ **A review log git would not merge cleanly -- `check` names it (F-090).** When the docs
187
+ root holds `audit/reviews/REVIEW_LOG.md` and git reports no `merge` attribute for it (no
188
+ committed `.gitattributes` line covers it: a project set up before the stanza, a custom
189
+ docs root, a project below the repository root, a course project), `check` prints one
190
+ `[note]` with the exact line to add at the repository root. Same contract as above:
191
+ informational, never the exit code, never a `validate` warning. Personal attribute files
192
+ (except the per-clone `.git/info/attributes`) are ignored, so the note matches what a
193
+ teammate's clone does; a deliberate driver other
194
+ than `union` is respected. Session orientation does not run it: it stays zero-execution.
195
+
185
196
  **Installing the package wires the hook machine-wide (F-042).** On Claude Code
186
197
  with Python, `npm i -g` (and every update) wires a SessionStart orient hook into
187
198
  the USER-level settings (`$CLAUDE_CONFIG_DIR/settings.json` when set, else
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: agentic-sdlc
3
- version: 1.50.0
3
+ version: 1.52.0
4
4
  description: Documentation-First SDLC protocol with risk-proportional triage, Vision as a guide, a complete Standalone mode and optional symbiosis with devPNT. Use for features, significant bugs, refactors, audits and documented maintenance.
5
5
  author: Antonio Pinto (https://github.com/Antoneeo)
6
6
  copyright: (c) 2026 Antonio Pinto
@@ -20,6 +20,7 @@ Support files in the skill directory:
20
20
  - `vision.md`: how to write a Vision a cold reviewer can actually apply — the properties that make a rule hold, the minimum operable sections, and the blind check run before promoting one to APPROVED.
21
21
  - `hybrid.md`: the devPNT seam — the authoritative hierarchy, the ownership matrix (who owns what when both authorities are live), the triage equivalence, the feature-state mapping and the shadow discipline. Read it when the `devpnt_*` tools point at this project; a Standalone session never needs it.
22
22
  - `routing.md`: which lens owns this unit of work. Read ONLY when a sibling lens skill is installed alongside this one; a single-lens install never reads it.
23
+ - `coordination.md`: several sessions on one repository, one of them coordinating. Read ONLY when the owner has named a coordinating session; otherwise never.
23
24
  - `scripts/sdlc_check.py` + `scripts/sdlc_core.py`: the mechanical validator for the docs root (`check`, `validate`, `index`, `stale`, `mark`, `benefit`, `gate`, `plan`, `orient`, `migrate`). Keep the entry point, `scripts/sdlc_core.py` and `scripts/knowledge.py` together; the last supplies common memory and KB integrity.
24
25
  - `scripts/docs_site.py --root <project> --open` (`--zip <file>` to share): opens in the user's browser a read-only site of what the agent reads; use it to show the project state.
25
26
  It is what users mean by "docs site", "minisito", "documentation site" or "project site": run it, never build a page by hand.
@@ -86,7 +87,7 @@ Triage decides IF documentation is due; this table decides WHICH document each e
86
87
  | Document | Write trigger | Phase |
87
88
  |---|---|---|
88
89
  | `solutions/ANALYSIS_[feature].md` | Every L3, after elicitation and before any code. On topic match with an existing analysis, update that one instead of a new file -- a revision re-reads the WHOLE document and rewrites it to current state (history in the Diary), never an appended delta section. A capability the architect pass splits out as its own unit of change (`architect.md` §4) gets its own ANALYSIS, and the two documents name each other. | 3 |
89
- | `audit/requests/REQUEST_[feature].md` | Every L3, the moment the level is declared — before the Vision Gate and the first question: R1 quotes the owner's opening words verbatim; every later owner statement that settles something for the unit (answer, approval, cost, ruling, merge) is appended before acting on it. Append-only, never deleted; artifacts cite its entries, a paraphrase never replacing the citation (`templates.md`). | 1 → 5 |
90
+ | `audit/requests/REQUEST_[feature].md` | Every L3, the moment the level is declared — before the Vision Gate and the first question: R1 quotes the owner's opening words verbatim; every later owner statement that settles something for the unit (answer, approval, cost, ruling, merge) is appended before acting on it. Append-only, never deleted; artifacts cite its entries, a paraphrase never replacing the citation (`templates.md`). Under coordination, which record an entry goes to: `coordination.md` §1. | 1 → 5 |
90
91
  | `solutions/SPIKE_[topic].md` | Closing any Spike — including a failed one (a negative outcome is still an outcome). | — |
91
92
  | `solutions/E2E_TEST_[feature].md` | Every L3, before the closure review (it is part of the reviewed diff): the human tester's post-merge script — new-behavior scenarios when behavior changed, regression scenarios always (`templates.md`). | 5 |
92
93
  | `solutions/MERGE_REVIEW_[feature].md` | Every L3, after the closure review: the integrator's brief — what to review (`sdlc_check.py review-map`), what was verified by which gate, what only a human can judge (`templates.md`). The PR description links it. | 5 |
@@ -207,12 +208,12 @@ Hybrid L3:
207
208
  ### 4. Development and Testing
208
209
 
209
210
  - Implement only after the documentation gate required by the level; at L3 it ends with the owner's design approval (`review.md` §When a review is due). **Flip the ANALYSIS frontmatter `PLANNED` → `IN_PROGRESS` when implementation starts** — that flip is what `gate` and the handoff registry key on, and nothing else performs it.
210
- - Isolate the work: run an L3 change on its own branch. In Hybrid, prefer a git worktree from the start — a running devPNT server locks `.devpnt/*.db` and blocks in-place branch switches/merges in the primary worktree. A session that may run while another session uses the same checkout works in its **own git worktree**, and never switches branch, stashes, resets or runs `index` in a checkout another session may be using. Parallel work also shares ids: choose a new unit's id **after fetching**, as the first id free on every branch and tag the checkout can see (`check` names a collision while the unit is open).
211
+ - Isolate the work: run an L3 change on its own branch. In Hybrid, prefer a git worktree from the start — a running devPNT server locks `.devpnt/*.db` and blocks in-place branch switches/merges in the primary worktree. A session that may run while another session uses the same checkout works in its **own git worktree**, and never switches branch, stashes, resets or runs `index` in a checkout another session may be using; under coordination, the sessions `coordination.md` §2 places in the shared checkout are the one exception to the own worktree — the bans hold there, and only its coordinator runs `index`. Parallel work also shares ids: choose a new unit's id **after fetching**, as the first id free on every branch and tag the checkout can see (`check` names a collision while the unit is open).
211
212
  - Modify surgically, consistently with the plan.
212
213
  - Implementation work follows the TDD discipline in `tdd.md` (RED/GREEN/REFACTOR — the L2/L3 default; record the reason when it does not apply).
213
214
  - Before implementing (L2/L3; L1 exempt), **consult the guide router** — the verdict already declared with the triage level (`## Operative Guides`). Re-run the lookup here only if the work has moved to a topic the first one did not cover, and say so if the verdict changes.
214
215
  - If the environment does not allow automated tests, declare the alternative verification and the reason.
215
- - **Constraint recall (L3).** Where the recall hook is wired (`ENFORCEMENT.md` §3), an edit of a file the open unit's constraints bind shows them first. Without it, run `sdlc_check.py constraints --file <path>` before its first edit of a file in a session.
216
+ - **Constraint recall (L3).** Where the recall hook is wired (`ENFORCEMENT.md` §3), an edit of a file the open unit's constraints bind shows them first. Without it — or for a file you write from the shell, which the hook never sees — run `sdlc_check.py constraints --file <path>` before its first edit of a file in a session.
216
217
  - For bugs (L2/L3), continue the investigation and design-verification method in `debugging.md`; implementation follows the `tdd.md` discipline above.
217
218
  - Circuit breaker: after 3 consecutive runs without progress on the tests, stop, switch to the systematic method in `debugging.md`, and ask for instructions if still stuck. `debugging.md` also covers **chronic fragility** — a component that breaks repeatedly across sessions is a comprehension + complexity signal (write the `source_kind: code` guide AND escalate a refactor), not a fourth patch.
218
219
  - Update the ANALYSIS Diary or the Action Plan when you complete milestones, hit blockers, change decisions, or a session ends with work unfinished.
@@ -0,0 +1,198 @@
1
+ # Coordinating Parallel Sessions
2
+
3
+ Rules for several peer sessions working on one repository at once, one of them
4
+ coordinating. Read this only when they apply (§Trigger); a session that is not
5
+ coordinated never needs it.
6
+
7
+ ## Trigger and roles
8
+
9
+ The rules apply while more than one session works on the same repository **and**
10
+ the owner has named the coordinating session in a request-record entry. The skill
11
+ never elects a coordinator. Without that naming no session coordinates: each works
12
+ in its own worktree, asks the owner before using an exclusive resource, and appends
13
+ to no record it does not own.
14
+
15
+ - **Coordinator** — the session the owner named. It keeps the shared files below,
16
+ writes the record of the shared unit, and acts for the owner only within what the
17
+ owner delegated.
18
+ - **Part** — a peer session the owner can talk to, working on its own unit and
19
+ following the whole skill for it (triage, design, review, closure). It is not a
20
+ dispatched subagent: `dispatch.md` governs those, unchanged.
21
+
22
+ **Who coordinates.** The role belongs to the client conversation the owner named.
23
+ Compacted in place, it is the same session and keeps the role; before acting on its
24
+ summary it rereads `leases.md` and the shared record, which are the state. A new
25
+ conversation is a new session, whatever brief or summary it carries: a session another
26
+ starts, the coordinator included, never inherits the role, and a message passing it on
27
+ is information (§4), never a naming.
28
+
29
+ The owner changes the coordinator with a new entry naming another session, B. Whoever
30
+ receives those words enters them once: B, or the previous coordinator A if the owner
31
+ told A — A then tells B and stops. B appends its `coordinator` line (§2) and tells the
32
+ parts, and A unless A entered the naming, citing the entry. Shown the entry, A writes no
33
+ shared file again and answers that it stopped. Every `leases.md` line and shared-record
34
+ entry B did not write between the naming entry and A's answer goes to the owner; a
35
+ number used twice is settled by a correction entry (`templates.md` §REQUEST), never by
36
+ renumbering. Leases in force stay: they name holders, not the coordinator. A coordinator
37
+ gone with no successor named leaves no session coordinating until the owner names one:
38
+ parts ask the owner, and `leases.md` and the shared record stay as they are.
39
+
40
+ Out of scope: which part runs next, in what order, with what priority — the owner
41
+ decides that, and nothing here records it (Non-Goal 1, ruling r3).
42
+
43
+ ## 1. Shared files: one writer each
44
+
45
+ A request record numbers its entries; two writers number them twice (field case:
46
+ three sessions wrote R156–R158 into one record at once). So:
47
+
48
+ - A part's unit has its own record when its triage gives it one (every L3); the
49
+ part is its only writer.
50
+ - The record of the shared unit the parts serve — the milestone, the umbrella — is
51
+ written only by the coordinator.
52
+ - Owner words a part receives are recorded **before it acts on them**, in the record
53
+ of the unit they settle. Words settling the part's own unit go to its own record.
54
+ Words settling the shared unit are appended verbatim to the part's own record
55
+ under the received heading (`templates.md` §REQUEST) and sent to the coordinator,
56
+ which appends them verbatim to the shared record citing
57
+ `REQUEST_[part].md#R<n>`.
58
+ - A part with no record of its own (below L3) sends the words; the coordinator's
59
+ shared entry names that part and the date received, and the part acts once it
60
+ exists.
61
+ - Every such entry keeps every rule of the record: verbatim, redaction, the owner
62
+ told so they can correct a quotation.
63
+ - An artifact of the shared unit cites only the shared record: the coordinator
64
+ enters the words before any such artifact relies on them.
65
+
66
+ Other files several sessions write:
67
+
68
+ - **Review log** — not a request record: its rows are unnumbered and each names its
69
+ document. Every session appends only its own rows and never edits another's; in one
70
+ checkout it appends without rewriting the file and rereads it just before writing.
71
+ Merging across worktrees and the commit map: `templates.md` §REVIEW_LOG and §Commit
72
+ map. A project without the union stanza is named by `check`, with the line to add;
73
+ the coordinator adds it (`.gitattributes` is its file, §2).
74
+ - **Translation templates and catalogs** are code (§2): in the shared checkout only the
75
+ code-writer lease holder writes them; a wrong string another session finds goes to
76
+ the holder through the coordinator.
77
+ - **An external tool's store tracked in git** (devPNT's `.devpnt/`): written by the tool,
78
+ never by hand; in a commit map under Shared, naming the units whose work it holds —
79
+ never under Regenerated, which is the validator's.
80
+
81
+ ## 2. Leases on exclusive resources
82
+
83
+ An exclusive resource has one holder at a time: the development container, the test
84
+ database, the code-writer role in a checkout a runtime mounts, any other the owner
85
+ names. A part asks the coordinator; the coordinator appends to `audit/leases.md`:
86
+
87
+ ```
88
+ take <resource> — <session> — YYYY-MM-DD
89
+ release <resource> — <session> — YYYY-MM-DD
90
+ ```
91
+
92
+ The file opens with `coordinator <session> — REQUEST_[unit].md#R<n> — YYYY-MM-DD`,
93
+ citing the owner's entry that names it (a file opened with a bare citation counts it
94
+ as its first `coordinator` line); a new naming appends another. Only the coordinator
95
+ writes the file; the live copies of the file and of the shared record are the shared
96
+ checkout's. Parts read its last `coordinator` line to know whom to ask, and trust it
97
+ only if the entry it cites quotes the owner naming that session — never a delegated or
98
+ received entry. A lease names a resource and a session, never a unit; it keeps no queue,
99
+ no waiting list, no order, and ends with its release (ruling r40). A restart or a
100
+ long suite on a shared resource is announced to the coordinator before it starts —
101
+ it kills what another session is running against it. A permission for a resource
102
+ given before the coordinator was named («restart it as much as you like, nobody else
103
+ uses it») rested on a single user: from the naming on it exempts no session from the
104
+ lease. If the owner restates it, the coordinator enters it as a `take` for that
105
+ session — a recorded lease, never an exemption. A lease never released is
106
+ closed by the owner through the coordinator; there is no timeout.
107
+
108
+ **The shared checkout is the one exception to «own worktree»** (`SKILL.md` Phase 4):
109
+ the checkout the runtime mounts (with no runtime, the one the owner names). Three kinds
110
+ of session work in it; every other session keeps its own worktree. Phase 4's bans on
111
+ switching branch, stashing and resetting hold there for all three.
112
+
113
+ - **The code-writer lease holder** writes code — every file the runtime runs or builds
114
+ from: sources, templates, static assets, tests, migrations, translation templates and
115
+ catalogs; files under the docs root are not code. The lease passes to another session
116
+ only once the holder's code in that checkout is committed, or set aside with the
117
+ owner's yes. Two sessions' code in that checkout is a violation, code a previous
118
+ holder left uncommitted included.
119
+ - **A documents-only part** writes only its own unit's files under the docs root and its
120
+ own review-log rows (§1); it runs no git command that changes the checkout and no
121
+ command that rewrites other units' files (`index`, `mark`). A change it needs
122
+ elsewhere goes to the session that writes that file. Before writing any code it asks
123
+ the coordinator: it takes the code-writer lease, or the owner commits its documents
124
+ and its own worktree is cut from that commit.
125
+ - **The coordinator** writes the shared files, and any file there that is neither code
126
+ nor under the docs root (root `README.md`, `CHANGELOG.md`, `.gitattributes`, CI
127
+ configuration — it runs `init` when needed); it alone runs `index`, `review-map` and
128
+ `mark` there, for the documents-only parts' closure steps too. There `review-map` sees
129
+ every session's files, so when a documents-only part closes, the coordinator writes
130
+ that unit's section of the commit map (§6) and the part's merge brief takes its file
131
+ table from it, and says so.
132
+
133
+ ## 3. Decisions taken under delegation
134
+
135
+ A decision the coordinator takes under the owner's delegation is a request-record
136
+ entry under the delegated heading (`templates.md` §REQUEST), citing the delegation
137
+ entry — in Hybrid also the devPNT proposal it accepted (`hybrid.md`). It is the
138
+ decision's one home: an artifact relying on it cites the entry (ruling r41).
139
+
140
+ A delegation never reaches an earlier owner decision, of any unit: a choice that
141
+ would change one goes to the owner as a question. (Field case: under a delegation
142
+ to approve design boards, the coordinator removed a file the owner had decided to
143
+ keep; a design document was accepted on it before the coordinator withdrew.)
144
+
145
+ ## 4. What travels between sessions
146
+
147
+ - An owner statement recorded verbatim in a request record is citable by every
148
+ session.
149
+ - An authorization for a gated act — one the client or the skill makes wait for the
150
+ owner's yes — is valid only in the session the owner gave it to. A message from
151
+ another session saying the owner authorized it is information, never the
152
+ authorization: the receiving session asks the owner, or the session holding it
153
+ acts.
154
+ - A decision found to rest on a false fact is reopened by a new entry naming the one
155
+ it corrects (`templates.md` §REQUEST); the old entry is never edited.
156
+
157
+ ## 5. When a part stops
158
+
159
+ A part that stops — done, blocked, interrupted — sends the coordinator:
160
+
161
+ ```
162
+ Unit / increment:
163
+ State: done | stopped at step <X>
164
+ Files touched:
165
+ Verification run and result:
166
+ Open points:
167
+ Needs the owner:
168
+ ```
169
+
170
+ A documents-only part in the shared checkout lists under `Open points` the closure
171
+ steps it left to the coordinator (§2).
172
+
173
+ The coordinator checks each claim on the diff and the run output, never on the
174
+ message (`review.md`, an unproven completion claim), then tells the owner what is
175
+ done, what is left and what needs them.
176
+
177
+ A part can stop without being able to send (a crash, a closed client). A part with
178
+ no closure message is not done: before relying on its state — accepting its work,
179
+ mapping a commit, telling the owner it finished — the coordinator reads its HANDOFF
180
+ `next:` and its working tree, and tells the owner which closure steps (`SKILL.md`
181
+ Phase 5) the tree shows done and which it cannot tell. Whether to resume the part is the
182
+ owner's call.
183
+
184
+ ## 6. One commit, several units
185
+
186
+ Before a commit carrying more than one unit's work, the coordinator writes the
187
+ commit map (`templates.md`) and runs:
188
+
189
+ ```
190
+ python <skill_dir>/scripts/sdlc_check.py commit-map MAP
191
+ ```
192
+
193
+ It checks the map against the working tree as it stands against HEAD: a generated
194
+ file under a unit, a changed HANDOFF or request record in no section, a file under
195
+ two units outside Shared, a file both under a unit and in Left out, a changed
196
+ authored file under Regenerated are errors; a
197
+ changed file in no section, a listed file with no change are warnings. It sees this
198
+ working tree only, not other worktrees. The commit stays the owner's.
@@ -34,11 +34,11 @@ proof that it caused this incident.
34
34
  Do not repeat an invalidated hypothesis unless new evidence reopens it.
35
35
 
36
36
  3. **Test and trace the mechanism.** Reproduce and isolate the smallest faithful
37
- failing case when feasible; bisect inputs, paths or changes when informative.
38
- Skip a reproduction already known to omit the incident's causal conditions;
39
- choose a faithful alternative or disclose the gap. Stop diagnostic checks when
40
- further results cannot change the causal or fix decision; this does not replace
41
- the regression and recovery verification in step 5.
37
+ failing case when feasible; bisect inputs, paths or changes when informative.
38
+ Skip a reproduction already known to omit the incident's causal conditions;
39
+ choose a faithful alternative or disclose the gap. Stop diagnostic checks when
40
+ further results cannot change the causal or fix decision; this does not replace
41
+ the regression and recovery verification in step 5.
42
42
  If the incident is intermittent or inaccessible, use captured traces and
43
43
  targeted diagnostic observations instead of declaring investigation impossible.
44
44
  Add minimal instrumentation when evidence is insufficient. Prefer read-only
@@ -86,7 +86,9 @@ neither a source nor a weighing is a finding (`review.md` §Reviewing).
86
86
  - **Unattended** — the user is not reachable (the Unattended path above).
87
87
  - **Delegated** — the user's own words handed you this choice ("your call"); cite
88
88
  their entry in the request record, or quote them where the unit has none (below
89
- L3). A general "go ahead" is not a delegation.
89
+ L3). A general "go ahead" is not a delegation. A choice you make under it goes, where the
90
+ unit has a request record, into it as a delegated entry (`templates.md` §REQUEST) and the artifact
91
+ cites that entry; it never changes an earlier decision of theirs — ask instead.
90
92
 
91
93
  Each states **what it is taken from** and **the alternative it excludes**, and is
92
94
  listed where the user will read it: an assumption recorded alone is a decision
@@ -467,7 +467,9 @@ When you are the reviewer:
467
467
  the artifact relies on — an answer, an approval, a cost acceptance, a ruling, a
468
468
  delegation — that resolves to no entry of the unit's request record, or that the
469
469
  record contradicts, is a finding; so is a quotation in the record carrying what
470
- its template says to redact. What makes a ground
470
+ its template says to redact, a delegated entry citing no delegation or changing
471
+ an earlier owner decision, and an artifact of a shared unit citing a received
472
+ entry instead of the shared record (`templates.md` §REQUEST, labelled entries). What makes a ground
471
473
  or an assumption legal is each lens's `elicitation.md` §The question discipline —
472
474
  cite it, never restate it. The fix is the question, not a better justification.
473
475
  - **Restated facts (cite, never copy).** Every governance slot has ONE owning document
@@ -41,7 +41,7 @@ sdlc_core.set_profile(
41
41
  unit_noun="feature",
42
42
  support_files=("templates.md", "architect.md", "guides.md", "vision.md", "tdd.md",
43
43
  "debugging.md", "elicitation.md", "review.md", "dispatch.md",
44
- "routing.md", "hybrid.md", "ENFORCEMENT.md", "memory.md"),
44
+ "routing.md", "coordination.md", "hybrid.md", "ENFORCEMENT.md", "memory.md"),
45
45
  capabilities=(
46
46
  # spine
47
47
  "triage", "write_triggers", "workstream_registry", "vision_gate",
@@ -84,7 +84,11 @@ REVIEW_INSTRUCTIONS = {
84
84
  REVIEW_MAP_HELP = ("\nCode lens:\n review-map [--base REF] [--root DIR] [--docs-dir NAME]\n"
85
85
  " classify every file changed since the merge-base with REF "
86
86
  "(default main):\n REVIEW / DESIGN / EVIDENCE / GENERATED, "
87
- "as a markdown table for the merge review brief")
87
+ "as a markdown table for the merge review brief\n"
88
+ " commit-map MAP [--root DIR] [--docs-dir NAME]\n"
89
+ " check a commit map (one commit carrying several units' work, "
90
+ "templates.md)\n against the working tree as it stands against "
91
+ "HEAD; sees this working tree only,\n not other worktrees")
88
92
 
89
93
 
90
94
  def table_cell(text):
@@ -268,10 +272,123 @@ def cmd_review_map(argv):
268
272
  return 0
269
273
 
270
274
 
275
+ # ------------------------------------------------------------- commit-map (F-088)
276
+ # The coordinator's map of ONE commit carrying several units' work: a section per
277
+ # unit, Shared, Regenerated, Left out. The grouping is a judgement, so the map is
278
+ # hand-written; this checks only what a tree can contradict. The base is HEAD:
279
+ # the map describes the next commit, not the branch. Paths from the map are
280
+ # compared with git's list as text, never opened.
281
+
282
+ COMMIT_MAP_SPECIAL = {"shared": "shared", "regenerated": "regenerated",
283
+ "left out": "left_out"}
284
+
285
+
286
+ def parse_commit_map(text):
287
+ """{path: [section, ...]} where a section is a unit heading, or one of
288
+ 'shared' / 'regenerated' / 'left_out'. An item is a list line whose first
289
+ code span is the path; text after it (a reason, the units sharing it) is free."""
290
+ import re
291
+ listed, section = {}, None
292
+ for line in text.splitlines():
293
+ if line.startswith("## "):
294
+ head = line[3:].strip()
295
+ section = COMMIT_MAP_SPECIAL.get(head.lower(), head)
296
+ continue
297
+ m = re.match(r"^\s*[-*] +`([^`]+)`", line)
298
+ if m and section is not None:
299
+ path = m.group(1).strip().replace("\\", "/")
300
+ while path.startswith("./"):
301
+ path = path[2:]
302
+ listed.setdefault(path, []).append(section)
303
+ return listed
304
+
305
+
306
+ def cmd_commit_map(argv):
307
+ import argparse
308
+ ap = argparse.ArgumentParser(prog="sdlc_check.py commit-map")
309
+ ap.add_argument("map")
310
+ ap.add_argument("--root")
311
+ ap.add_argument("--docs-dir", dest="docs_dir")
312
+ args = ap.parse_args(argv)
313
+ map_path = Path(args.map).resolve()
314
+ try:
315
+ text = map_path.read_text(encoding="utf-8")
316
+ except (OSError, UnicodeDecodeError) as exc:
317
+ print(f"[ERROR] cannot read the commit map {table_cell(args.map)}: {exc}")
318
+ return 1
319
+ try:
320
+ discovered, name = sdlc_core.resolve_docs_dir(args, args.root)
321
+ except sdlc_core.AmbiguousDocsRoot as exc:
322
+ print(f"[ERROR] {exc}")
323
+ return 1
324
+ sdlc_core.set_docs_dir(name)
325
+ root = Path(args.root).resolve() if args.root else (discovered or sdlc_core.find_project_root())
326
+ if not sdlc_core.require_ai_docs(root, "commit-map"):
327
+ return 1
328
+ top = _git(root, "rev-parse", "--show-toplevel")
329
+ if top.returncode != 0:
330
+ print(f"[ERROR] {root} is not inside a git repository: commit-map compares the map with HEAD.")
331
+ return 1
332
+ top = Path(top.stdout.strip()).resolve()
333
+ if _git(top, "rev-parse", "--verify", "--quiet", "HEAD^{commit}").returncode != 0:
334
+ print("[ERROR] the repository has no commit yet: commit-map compares the map with HEAD.")
335
+ return 1
336
+ docs = sdlc_core.ai_path(root).resolve()
337
+ try:
338
+ docs_prefix = docs.relative_to(top).as_posix()
339
+ except ValueError:
340
+ print(f"[ERROR] {docs} is outside the git repository {top}.")
341
+ return 1
342
+ try:
343
+ own = map_path.relative_to(top).as_posix()
344
+ except ValueError:
345
+ own = None
346
+ changed = {p for _c, p, _a, _d in _changes(top, "HEAD") if p != own}
347
+ generated = set()
348
+ if any(classify_path(p, docs_prefix, set()) != "REVIEW" for p in changed):
349
+ generated = _generated_set(docs)
350
+ if generated is None:
351
+ return 1
352
+ listed = parse_commit_map(text)
353
+ special = set(COMMIT_MAP_SPECIAL.values())
354
+ errors, warnings = [], []
355
+ for path in sorted(set(changed) | set(listed)):
356
+ sections = listed.get(path, [])
357
+ units = [s for s in sections if s not in special]
358
+ is_generated = classify_path(path, docs_prefix, generated) == "GENERATED"
359
+ rel = path[len(docs_prefix) + 1:] if path.startswith(docs_prefix + "/") else ""
360
+ if units and is_generated:
361
+ errors.append((path, "regenerated by the validator, listed under a unit: "
362
+ "move it to Regenerated"))
363
+ if len(set(units)) > 1 and "shared" not in sections:
364
+ errors.append((path, "under " + ", ".join(sorted(set(units)))
365
+ + " outside Shared: move it to Shared"))
366
+ if units and "left_out" in sections:
367
+ errors.append((path, "both under a unit and in Left out"))
368
+ if "regenerated" in sections and path in changed and not is_generated:
369
+ errors.append((path, "under Regenerated, but the validator does not write it: "
370
+ "list it under its unit"))
371
+ if path in changed and not sections:
372
+ if rel.startswith("audit/HANDOFF_") or rel.startswith("audit/requests/"):
373
+ errors.append((path, "a changed HANDOFF or request record in no section"))
374
+ else:
375
+ warnings.append((path, "changed, in no section"))
376
+ if path not in changed:
377
+ warnings.append((path, "listed, but not changed against HEAD"))
378
+ for level, items in (("ERROR", errors), ("WARN", warnings)):
379
+ for path, rule in items:
380
+ print(f"[{level}] `{table_cell(path)}` -- {rule}")
381
+ print(f"commit-map: {len(changed)} changed files against HEAD; "
382
+ f"{len(errors)} errors, {len(warnings)} warnings")
383
+ return 1 if errors else 0
384
+
385
+
271
386
  def main(argv=None):
272
387
  argv = list(sys.argv[1:] if argv is None else argv)
273
388
  if argv[:1] == ["review-map"]:
274
389
  return cmd_review_map(argv[1:])
390
+ if argv[:1] == ["commit-map"]:
391
+ return cmd_commit_map(argv[1:])
275
392
  rc = knowledge.main(argv)
276
393
  if argv[:1] in (["-h"], ["--help"]):
277
394
  print(REVIEW_MAP_HELP)
@@ -1377,9 +1377,12 @@ def _open_units(root):
1377
1377
  return units
1378
1378
 
1379
1379
 
1380
- def _git_out(root, *args, timeout=30):
1380
+ def _git_out(root, *args, timeout=30, env=None):
1381
+ # `env` is merged into the inherited environment, never a replacement: a bare
1382
+ # dict would drop PATH and fail silently on Windows.
1381
1383
  r = subprocess.run(["git", *args], cwd=str(root), capture_output=True, text=True,
1382
- encoding="utf-8", errors="replace", timeout=timeout)
1384
+ encoding="utf-8", errors="replace", timeout=timeout,
1385
+ env={**os.environ, **env} if env else None)
1383
1386
  return r.stdout if r.returncode == 0 else None
1384
1387
 
1385
1388
 
@@ -2481,10 +2484,64 @@ def cmd_check(root, strict=False, hybrid=False, extra_validate=None, extra_secti
2481
2484
  failed = any(rc for _, rc in rcs)
2482
2485
  print(f"\ncheck: {'NOT CLEAN' if failed else 'CLEAN'} "
2483
2486
  f"({', '.join(f'{title} rc={rc}' for title, rc in rcs)})")
2484
- print_orient_hook_note(root)
2487
+ print_check_notes(root)
2485
2488
  return 1 if failed else 0
2486
2489
 
2487
2490
 
2491
+ def _attr_pattern(path):
2492
+ """A .gitattributes pattern matching exactly `path`. Order matters (probed):
2493
+ a backslash, glob characters and a leading `#` or `!` (comment, negation)
2494
+ are escaped first, then the result is C-quoted when it holds whitespace,
2495
+ `"` or a backslash."""
2496
+ p = re.sub(r"([\\\[*?])", r"\\\1", path)
2497
+ if p.startswith(("#", "!")):
2498
+ p = "\\" + p
2499
+ if re.search(r'[\s"\\]', p):
2500
+ p = '"' + p.replace("\\", "\\\\").replace('"', '\\"') + '"'
2501
+ return p
2502
+
2503
+
2504
+ def review_log_union_note(root):
2505
+ """F-090: the note for a review log git would not merge with union, or None.
2506
+
2507
+ Git itself is asked (`check-attr`), so patterns and nested files count;
2508
+ personal and system attribute files do not, so the answer is the one a
2509
+ teammate's clone gets. Only `unspecified` is a gap: `union` or any other
2510
+ driver is a choice. Residual: `.git/info/attributes` is still read. Any
2511
+ failure -- no git, not a work tree, timeout -- is no note: `check` never
2512
+ breaks on it."""
2513
+ try:
2514
+ log = ai_path(root, "audit", "reviews", "REVIEW_LOG.md")
2515
+ if not log.is_file():
2516
+ return None
2517
+ prefix = _git_out(root, "rev-parse", "--show-prefix", timeout=3)
2518
+ if prefix is None:
2519
+ return None
2520
+ rel = log.resolve().relative_to(Path(root).resolve()).as_posix()
2521
+ out = _git_out(root, "-c", "core.attributesFile=", "check-attr", "-z", "merge",
2522
+ "--", rel, timeout=3, env={"GIT_ATTR_NOSYSTEM": "1"})
2523
+ if out is None:
2524
+ return None
2525
+ fields = out.split("\0")
2526
+ if len(fields) < 3 or fields[2] != "unspecified":
2527
+ return None
2528
+ path = prefix.rstrip("\r\n") + rel # a folder may begin with a space
2529
+ return (f"[note] {path} has no merge attribute: two branches that both add review "
2530
+ "rows conflict when merged. Fix: add this line to .gitattributes at the "
2531
+ f"repository root: {_attr_pattern(path)} merge=union")
2532
+ except Exception:
2533
+ return None
2534
+
2535
+
2536
+ def print_check_notes(root):
2537
+ """Every check-layer note, informational and never an exit code. The one
2538
+ place an overlay that replaces `check` calls, so a new note cannot miss it."""
2539
+ print_orient_hook_note(root)
2540
+ note = review_log_union_note(root)
2541
+ if note:
2542
+ print(note)
2543
+
2544
+
2488
2545
  # --------------------------------------------------------------------- gate
2489
2546
 
2490
2547
  # --- UserPromptSubmit reminder hook (F-046) --------------------------------
@@ -549,12 +549,58 @@ Context (agent): "the question it answered, quoted"
549
549
  entered only as `[NOT VERBATIM — from <source>]`, never as a quote.
550
550
  - **Unattended**: R1 quotes the trigger that started the unit, labelled as such.
551
551
  Declared assumptions are not owner statements and never enter the record.
552
+ - **Labelled entries** (r41): a decision taken under the owner's delegation is
553
+ `### R<n> — YYYY-MM-DD — decided under delegation R<m> — <what it settles>`, citing
554
+ the delegation entry (in Hybrid also the devPNT proposal it accepted); it is the
555
+ deciding session's choice, never an owner statement, and never changes an earlier
556
+ owner decision — that goes to the owner as a question. Owner words received for a
557
+ unit whose record another session writes are
558
+ `### R<n> — YYYY-MM-DD — received for <unit> — <what it settles>`, then sent to that
559
+ record's writer, who enters them verbatim citing this entry. Several sessions on one
560
+ repository: `coordination.md` §1.
552
561
  - **Design approval** (L3): the owner's approval of the design is an entry whose
553
562
  label begins `design approved`; a no or a correction takes its own label
554
563
  (`review.md` §When a review is due).
555
564
  - **Hybrid**: always on the filesystem; governed artifacts cite entries too; an
556
565
  approval given in devPNT is named by its proposal, not quoted.
557
566
 
567
+ ## ai_docs/audit/leases.md (several sessions, one coordinating)
568
+
569
+ Written only by the coordinating session (`coordination.md` §2); append-only.
570
+
571
+ ```markdown
572
+ # Leases
573
+
574
+ coordinator coordinating session — REQUEST_[unit].md#R<n> — YYYY-MM-DD
575
+
576
+ take dev-container — wave-6 session — YYYY-MM-DD
577
+ release dev-container — wave-6 session — YYYY-MM-DD
578
+ ```
579
+
580
+ ## Commit map (one commit, several units)
581
+
582
+ Written by the coordinating session before a commit carrying more than one unit's
583
+ work, checked with `sdlc_check.py commit-map MAP` (`coordination.md` §6); not
584
+ committed. Each item is a list line whose first code span is the path.
585
+
586
+ ```markdown
587
+ # Commit map: [what this commit carries]
588
+
589
+ ## F-001 [unit]
590
+ - `src/app.py`
591
+ - `ai_docs/audit/HANDOFF_[feature].md`
592
+
593
+ ## Shared
594
+ - `ai_docs/audit/reviews/REVIEW_LOG.md` — F-001, F-002
595
+ - `.devpnt/plans/plans.db` — F-001, F-002 (written by devPNT)
596
+
597
+ ## Regenerated
598
+ - `ai_docs/INDEX.md`
599
+
600
+ ## Left out
601
+ - `scratch/notes.md` — why it is not in this commit
602
+ ```
603
+
558
604
  ## ai_docs/solutions/E2E_TEST_[feature].md
559
605
 
560
606
  Every L3, written before the closure review so it is part of the reviewed diff.
@@ -764,7 +810,7 @@ Concurrent reviews: `init` writes a `.gitattributes` stanza giving this file
764
810
  Rows are date-stamped and their order carries no meaning, so a union merge keeps
765
811
  both sides instead of asking a human to choose. It is defence in depth: without
766
812
  git, or without the stanza, the outcome is today's — one conflict you resolve by
767
- hand, never a lost row.
813
+ hand, never a lost row. `check` names a missing stanza and the line to add (F-090).
768
814
 
769
815
 
770
816
  ## ai_docs/audit/HANDOFF_[feature].md — one open workstream, its own file