@ikon85/agent-workflow-kit 0.15.0 → 0.16.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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: setup-workflow
3
- description: "Scaffolds the project layer the portable workflow skills assume — issue tracker, triage labels, domain-doc layout, GitHub-Projects board field-IDs, spec-self-critique + spec-completeness seeds, workflow overview, and the deploy target. Writes `docs/agents/*`, `docs/conventions/spec-completeness.md`, and the `## Workflow` / `## Agent skills` / `## Prod` blocks in CLAUDE.md/AGENTS.md. Idempotent: a re-run reconciles per file/section and never overwrites filled content. Run once after installing the skills (or `npx <pkg> init`), or when a skill reports missing project-layer context. Adapted from Matt Pocock's `setup-matt-pocock-skills` (MIT)."
3
+ description: "Scaffolds the project layer the portable workflow skills assume — issue tracker, triage labels, domain-doc layout, board field IDs, spec seeds, workflow overview, optional census choice, and deploy target. Writes `docs/agents/*`, `docs/conventions/spec-completeness.md`, and the `## Workflow` / `## Agent skills` / `## Prod` blocks in CLAUDE.md/AGENTS.md. Idempotent: a re-run reconciles per file/section and never overwrites filled content. Run once after installing the skills (or `npx <pkg> init`), or when a skill reports missing project-layer context. Adapted from Matt Pocock's `setup-matt-pocock-skills` (MIT)."
4
4
  disable-model-invocation: true
5
5
  ---
6
6
 
@@ -25,6 +25,7 @@ This is a prompt-driven skill, not a deterministic script. Explore, present what
25
25
  | `## Workflow` in CLAUDE.md **and** AGENTS.md | generic entry-point map seeded from [workflow-overview.md](./workflow-overview.md) (Section F + Write) |
26
26
  | `## Agent skills` + `## Prod` in CLAUDE.md **and** AGENTS.md | one-line pointers + deploy target (Sections C/G + Write) |
27
27
  | `.github/workflows/agent-workflow-kit-update.yml` | optional tested Kit update pull request for GitHub consumers (Section A2) |
28
+ | `docs/agents/census.md` | optional-census choice, paths, state, and safe disable contract seeded from [census.md](./census.md) (Section A3) |
28
29
 
29
30
  ## Idempotency contract — read before writing anything
30
31
 
@@ -54,6 +55,7 @@ Read the current state; don't assume. For every target file, read its first line
54
55
  - `CLAUDE.md` and `AGENTS.md` at the repo root — which exist? Do they already have a `## Workflow` / `## Agent skills` / `## Prod` block?
55
56
  - `CONTEXT.md`, `CONTEXT-MAP.md`, `docs/adr/` — domain-doc layout.
56
57
  - `docs/agents/`, `docs/agents/skills/`, `docs/conventions/` — prior output of this skill.
58
+ - `docs/agents/census.md`, `.census/profile.json`, `.census/active.json` — an existing census choice or consumer-owned census to adopt.
57
59
  - `gh auth status` (if GitHub) — is `gh` authenticated, and with which scopes?
58
60
 
59
61
  ### 2. Section A — Issue tracker
@@ -105,6 +107,34 @@ For GitLab, local, other, or an unknown provider, give only a provider-neutral e
105
107
 
106
108
  **Conditional board-write note (only when the GitHub tracker uses a managed board):** if Section D ends with `board-sync.md` at `mode: github-projects-v2`, add one line to `issue-tracker.md`: *"Board writes (item-add, status/wave/cluster field edits, sub-issue links) go through the board-sync helper, not bare `gh issue create`/`gh project item-*`."* Do **not** add this for GitLab, local, other, or `mode: none`.
107
109
 
110
+ ### 2b. Section A3 — Optional project census
111
+
112
+ > A project census is an optional, consumer-owned map that counts product
113
+ > surfaces and lists behavior families separately. It can make later plans and
114
+ > handoffs more complete, but setup cannot honestly claim coverage before the
115
+ > repository has been scanned and verified.
116
+
117
+ Read [census.md](./census.md) in full before presenting the choice. If
118
+ `docs/agents/census.md` already records `yes`, `later`, or `no`, adopt that
119
+ choice and do not prompt again on an ordinary setup rerun. Also adopt an
120
+ existing, explicitly documented census path. If no choice exists, ask in plain
121
+ language: *"Should setup prepare the optional project census now?"* Offer
122
+ exactly:
123
+
124
+ - **Yes** — create or adopt the project layer and minimal enabled profile, run
125
+ only the shipped self-test, and report the honest `bootstrap` / "not yet
126
+ meaningful" state. Do not scan, activate, or install a hook or gate.
127
+ - **Later** — record a deferral. Create no census profile, hook, or gate; a
128
+ later explicit `census-update` invocation may activate without setup.
129
+ - **No** — record the opt-out as `disabled`. Create no census profile, hook, or
130
+ gate and do not prompt again unless the user explicitly changes the choice.
131
+
132
+ Use the complete `missing / yes / later / no / existing / explicit-enable /
133
+ disable` matrix in the seed. A later explicit `census-update` invocation may
134
+ activate without rerunning setup. Disable enforcement first, but retain every
135
+ consumer-owned profile, scanner, test, and active snapshot unless the user
136
+ separately approves deletion. Repeated runs are no-ops after reconciliation.
137
+
108
138
  ### 3. Section B — Triage labels
109
139
 
110
140
  > When `triage` processes an incoming issue it applies labels (or your tracker's equivalent). It needs strings you've actually configured, or it creates duplicates.
@@ -182,6 +212,14 @@ Seed `docs/agents/code-review.md` from [code-review.md](./code-review.md) — th
182
212
 
183
213
  For each `docs/...` file: obey the idempotency contract (the "Idempotency contract" section). Prepend the sentinel with the resolved `state` (and `mode` for board-sync).
184
214
 
215
+ For `docs/agents/census.md`, seed [census.md](./census.md), prepend the normal
216
+ sentinel, and record the selected choice directly below it as
217
+ `<!-- census: choice=<yes|later|no> -->`. On adoption, also record the discovered
218
+ repository-relative profile and active-snapshot paths. Never overwrite a
219
+ pre-existing consumer-owned census file. `yes`, `later`, `no`, and an adopted
220
+ existing census are terminal for ordinary setup reruns. Only an explicit user
221
+ request changes a recorded choice.
222
+
185
223
  For the **`## Workflow`**, **`## Agent skills`**, and **`## Prod`** blocks, reconcile per section in **both** CLAUDE.md and AGENTS.md that exist:
186
224
 
187
225
  - If **both** files exist → write/update the block in **both** (keep them coherent — Codex is a first-class surface).
@@ -0,0 +1,83 @@
1
+ # Optional project census
2
+
3
+ The census is a consumer-owned, counted map of product surfaces and behavior
4
+ families. It is optional. Setup records only the user's choice; `census-update`
5
+ does the factual scan, resolves ambiguity, verifies a candidate, and activates
6
+ the result transactionally.
7
+
8
+ ## Setup state matrix
9
+
10
+ | State or choice | Setup action | Observable result on repeat |
11
+ |---|---|---|
12
+ | `missing` | Explain the census in plain language and ask `yes / later / no`; do not infer an answer. | Ask again only while no choice has been recorded; write no hook or gate. |
13
+ | `yes` | Create the minimal consumer-owned profile, or adopt the existing documented profile path. Set `enabled: true`, keep the active snapshot absent, run only the shipped census self-test, and report `bootstrap` / "not yet meaningful". | Adopt the same profile byte-for-byte; do not rescan, activate, or add another self-test. |
14
+ | `later` | Record a retryable deferral outside the active profile. | Leave census files, hooks, and gates absent; an ordinary setup rerun is a no-op, while an explicit `census-update` may activate later. |
15
+ | `no` | Record an explicit opt-out with census state `disabled`. | Keep the opt-out and do not create census files, hooks, or gates. |
16
+ | `existing` | Adopt the repository's explicitly documented profile and active-snapshot paths without replacing consumer-owned content. Derive its state through the public census API. | Preserve every existing byte and report the derived state. |
17
+ | `explicit-enable` | On a later explicit `census-update` invocation, let that skill scan, decide, verify, and transactionally activate without rerunning setup. | An unchanged verified census reports `current` and performs no write. |
18
+ | `disable` | Set the profile to `enabled: false` transactionally and remove census hooks/gates from enforcement. | Stay `disabled`; retain consumer-owned profiles, scanners, tests, and snapshots unless the user separately approves their deletion. |
19
+
20
+ ## Deterministic setup effects
21
+
22
+ The table below is the machine-checkable reference contract for setup. Its
23
+ ordered `operations` are the only effects the setup proof may execute. Choice
24
+ persistence always means `docs/agents/census.md`: the normal setup sentinel is
25
+ the first line and `<!-- census: choice=<yes|later|no> -->` is the second.
26
+ Paths under `retain` are consumer-owned evidence. Enforcement operations refer
27
+ only to kit-owned wiring documented by the consumer; they are not permission to
28
+ invent another census engine.
29
+
30
+ ```json census-setup-effects
31
+ [
32
+ {"state":"missing","actor":"setup","choice":"none","operations":[],"retain":[],"repeat":"no-write"},
33
+ {"state":"yes","actor":"setup","choice":"yes","operations":["reconcile-choice-doc","reconcile-minimal-profile","derive-state","run-foundation-self-test"],"retain":[],"repeat":"no-write"},
34
+ {"state":"later","actor":"setup","choice":"later","operations":["reconcile-choice-doc"],"retain":[],"repeat":"no-write"},
35
+ {"state":"no","actor":"setup","choice":"no","operations":["reconcile-choice-doc","derive-state"],"retain":[],"repeat":"no-write"},
36
+ {"state":"existing","actor":"setup","choice":"recorded","operations":["adopt-choice-doc","derive-state"],"retain":["choice-doc","profile","active","scanner","scanner-test"],"repeat":"no-write"},
37
+ {"state":"explicit-enable","actor":"census-update","choice":"recorded","operations":["delegate-census-update","run-census-update-contract"],"retain":["choice-doc","profile","active","scanner","scanner-test"],"repeat":"no-write"},
38
+ {"state":"disable","actor":"census-update","choice":"recorded","operations":["remove-kit-hook","remove-kit-gate","update-profile-disabled","derive-state"],"retain":["choice-doc","profile-unknown-keys","active","scanner","scanner-test"],"repeat":"no-write"}
39
+ ]
40
+ ```
41
+
42
+ ## Bootstrap profile
43
+
44
+ When `yes` creates the default `.census/profile.json`, write the deterministic
45
+ profile shape documented by `census-update`: `schemaVersion: 1`, `enabled:
46
+ true`, and empty `decisions`, `localScanners`, and `overrides` arrays. Do not
47
+ create `.census/active.json`. Its absence is the evidence that the honest state
48
+ is `bootstrap`, not `current`.
49
+
50
+ Before writing, check for an existing repository convention and adopt it. A
51
+ pre-existing profile remains consumer-owned: preserve unknown keys and do not
52
+ replace its decisions. Never derive `current` from file presence; use
53
+ `resolveCensusState` from `scripts/census/index.mjs`.
54
+
55
+ Run the focused `scripts/census/state.test.mjs` census foundation self-test
56
+ already shipped with the kit.
57
+ A passing self-test proves the mechanism is available, not that this repository
58
+ has been scanned. Setup must not install pre-commit, pre-push, CI, planning, or
59
+ handoff gates for `yes`, `later`, or `no`.
60
+
61
+ ## Later activation and disable
62
+
63
+ `later` and `no` are setup choices, not partially active censuses. A later,
64
+ explicit `census-update` invocation is the sole activation route. Setup
65
+ delegates this route to the shipped `census-update` contract and its focused
66
+ `scripts/test_census_update_contract.test.mjs` proof; it does not reproduce
67
+ activation, snapshots, or enforcement. Setup itself never calls `activateCensus`.
68
+
69
+ Disable follows the ordered contract: remove the kit-owned hook, remove the
70
+ kit-owned gate, then atomically replace only the profile's `enabled` value with
71
+ `false`, preserving unknown keys, and verify `disabled` through
72
+ `resolveCensusState`. Enforcement removal must finish before any profile
73
+ mutation. Treat the choice document, local scanners, their tests, the profile,
74
+ and the active snapshot as consumer-owned evidence. List those files and ask
75
+ for separate deletion approval; without that approval, retain them.
76
+ Setup never deletes consumer-owned files as part of disable.
77
+
78
+ ## Setup report
79
+
80
+ Report the choice, adopted or created paths, derived state, self-test result,
81
+ and whether enforcement changed. Name every action skipped on an idempotent
82
+ rerun. Never claim surface coverage during setup: only `census-update` can
83
+ produce a verified `X of Y` result.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: setup-workflow
3
- description: "Scaffolds the project layer the portable workflow skills assume — issue tracker, triage labels, domain-doc layout, GitHub-Projects board field-IDs, spec-self-critique + spec-completeness seeds, workflow overview, and the deploy target. Writes `docs/agents/*`, `docs/conventions/spec-completeness.md`, and the `## Workflow` / `## Agent skills` / `## Prod` blocks in CLAUDE.md/AGENTS.md. Idempotent: a re-run reconciles per file/section and never overwrites filled content. Run once after installing the skills (or `npx <pkg> init`), or when a skill reports missing project-layer context. Adapted from Matt Pocock's `setup-matt-pocock-skills` (MIT)."
3
+ description: "Scaffolds the project layer the portable workflow skills assume — issue tracker, triage labels, domain-doc layout, board field IDs, spec seeds, workflow overview, optional census choice, and deploy target. Writes `docs/agents/*`, `docs/conventions/spec-completeness.md`, and the `## Workflow` / `## Agent skills` / `## Prod` blocks in CLAUDE.md/AGENTS.md. Idempotent: a re-run reconciles per file/section and never overwrites filled content. Run once after installing the skills (or `npx <pkg> init`), or when a skill reports missing project-layer context. Adapted from Matt Pocock's `setup-matt-pocock-skills` (MIT)."
4
4
  disable-model-invocation: true
5
5
  ---
6
6
 
@@ -25,6 +25,7 @@ This is a prompt-driven skill, not a deterministic script. Explore, present what
25
25
  | `## Workflow` in CLAUDE.md **and** AGENTS.md | generic entry-point map seeded from [workflow-overview.md](./workflow-overview.md) (Section F + Write) |
26
26
  | `## Agent skills` + `## Prod` in CLAUDE.md **and** AGENTS.md | one-line pointers + deploy target (Sections C/G + Write) |
27
27
  | `.github/workflows/agent-workflow-kit-update.yml` | optional tested Kit update pull request for GitHub consumers (Section A2) |
28
+ | `docs/agents/census.md` | optional-census choice, paths, state, and safe disable contract seeded from [census.md](./census.md) (Section A3) |
28
29
 
29
30
  ## Idempotency contract — read before writing anything
30
31
 
@@ -54,6 +55,7 @@ Read the current state; don't assume. For every target file, read its first line
54
55
  - `CLAUDE.md` and `AGENTS.md` at the repo root — which exist? Do they already have a `## Workflow` / `## Agent skills` / `## Prod` block?
55
56
  - `CONTEXT.md`, `CONTEXT-MAP.md`, `docs/adr/` — domain-doc layout.
56
57
  - `docs/agents/`, `docs/agents/skills/`, `docs/conventions/` — prior output of this skill.
58
+ - `docs/agents/census.md`, `.census/profile.json`, `.census/active.json` — an existing census choice or consumer-owned census to adopt.
57
59
  - `gh auth status` (if GitHub) — is `gh` authenticated, and with which scopes?
58
60
 
59
61
  ### 2. Section A — Issue tracker
@@ -105,6 +107,34 @@ For GitLab, local, other, or an unknown provider, give only a provider-neutral e
105
107
 
106
108
  **Conditional board-write note (only when the GitHub tracker uses a managed board):** if Section D ends with `board-sync.md` at `mode: github-projects-v2`, add one line to `issue-tracker.md`: *"Board writes (item-add, status/wave/cluster field edits, sub-issue links) go through the board-sync helper, not bare `gh issue create`/`gh project item-*`."* Do **not** add this for GitLab, local, other, or `mode: none`.
107
109
 
110
+ ### 2b. Section A3 — Optional project census
111
+
112
+ > A project census is an optional, consumer-owned map that counts product
113
+ > surfaces and lists behavior families separately. It can make later plans and
114
+ > handoffs more complete, but setup cannot honestly claim coverage before the
115
+ > repository has been scanned and verified.
116
+
117
+ Read [census.md](./census.md) in full before presenting the choice. If
118
+ `docs/agents/census.md` already records `yes`, `later`, or `no`, adopt that
119
+ choice and do not prompt again on an ordinary setup rerun. Also adopt an
120
+ existing, explicitly documented census path. If no choice exists, ask in plain
121
+ language: *"Should setup prepare the optional project census now?"* Offer
122
+ exactly:
123
+
124
+ - **Yes** — create or adopt the project layer and minimal enabled profile, run
125
+ only the shipped self-test, and report the honest `bootstrap` / "not yet
126
+ meaningful" state. Do not scan, activate, or install a hook or gate.
127
+ - **Later** — record a deferral. Create no census profile, hook, or gate; a
128
+ later explicit `census-update` invocation may activate without setup.
129
+ - **No** — record the opt-out as `disabled`. Create no census profile, hook, or
130
+ gate and do not prompt again unless the user explicitly changes the choice.
131
+
132
+ Use the complete `missing / yes / later / no / existing / explicit-enable /
133
+ disable` matrix in the seed. A later explicit `census-update` invocation may
134
+ activate without rerunning setup. Disable enforcement first, but retain every
135
+ consumer-owned profile, scanner, test, and active snapshot unless the user
136
+ separately approves deletion. Repeated runs are no-ops after reconciliation.
137
+
108
138
  ### 3. Section B — Triage labels
109
139
 
110
140
  > When `triage` processes an incoming issue it applies labels (or your tracker's equivalent). It needs strings you've actually configured, or it creates duplicates.
@@ -182,6 +212,14 @@ Seed `docs/agents/code-review.md` from [code-review.md](./code-review.md) — th
182
212
 
183
213
  For each `docs/...` file: obey the idempotency contract (the "Idempotency contract" section). Prepend the sentinel with the resolved `state` (and `mode` for board-sync).
184
214
 
215
+ For `docs/agents/census.md`, seed [census.md](./census.md), prepend the normal
216
+ sentinel, and record the selected choice directly below it as
217
+ `<!-- census: choice=<yes|later|no> -->`. On adoption, also record the discovered
218
+ repository-relative profile and active-snapshot paths. Never overwrite a
219
+ pre-existing consumer-owned census file. `yes`, `later`, `no`, and an adopted
220
+ existing census are terminal for ordinary setup reruns. Only an explicit user
221
+ request changes a recorded choice.
222
+
185
223
  For the **`## Workflow`**, **`## Agent skills`**, and **`## Prod`** blocks, reconcile per section in **both** CLAUDE.md and AGENTS.md that exist:
186
224
 
187
225
  - If **both** files exist → write/update the block in **both** (keep them coherent — Codex is a first-class surface).
@@ -0,0 +1,83 @@
1
+ # Optional project census
2
+
3
+ The census is a consumer-owned, counted map of product surfaces and behavior
4
+ families. It is optional. Setup records only the user's choice; `census-update`
5
+ does the factual scan, resolves ambiguity, verifies a candidate, and activates
6
+ the result transactionally.
7
+
8
+ ## Setup state matrix
9
+
10
+ | State or choice | Setup action | Observable result on repeat |
11
+ |---|---|---|
12
+ | `missing` | Explain the census in plain language and ask `yes / later / no`; do not infer an answer. | Ask again only while no choice has been recorded; write no hook or gate. |
13
+ | `yes` | Create the minimal consumer-owned profile, or adopt the existing documented profile path. Set `enabled: true`, keep the active snapshot absent, run only the shipped census self-test, and report `bootstrap` / "not yet meaningful". | Adopt the same profile byte-for-byte; do not rescan, activate, or add another self-test. |
14
+ | `later` | Record a retryable deferral outside the active profile. | Leave census files, hooks, and gates absent; an ordinary setup rerun is a no-op, while an explicit `census-update` may activate later. |
15
+ | `no` | Record an explicit opt-out with census state `disabled`. | Keep the opt-out and do not create census files, hooks, or gates. |
16
+ | `existing` | Adopt the repository's explicitly documented profile and active-snapshot paths without replacing consumer-owned content. Derive its state through the public census API. | Preserve every existing byte and report the derived state. |
17
+ | `explicit-enable` | On a later explicit `census-update` invocation, let that skill scan, decide, verify, and transactionally activate without rerunning setup. | An unchanged verified census reports `current` and performs no write. |
18
+ | `disable` | Set the profile to `enabled: false` transactionally and remove census hooks/gates from enforcement. | Stay `disabled`; retain consumer-owned profiles, scanners, tests, and snapshots unless the user separately approves their deletion. |
19
+
20
+ ## Deterministic setup effects
21
+
22
+ The table below is the machine-checkable reference contract for setup. Its
23
+ ordered `operations` are the only effects the setup proof may execute. Choice
24
+ persistence always means `docs/agents/census.md`: the normal setup sentinel is
25
+ the first line and `<!-- census: choice=<yes|later|no> -->` is the second.
26
+ Paths under `retain` are consumer-owned evidence. Enforcement operations refer
27
+ only to kit-owned wiring documented by the consumer; they are not permission to
28
+ invent another census engine.
29
+
30
+ ```json census-setup-effects
31
+ [
32
+ {"state":"missing","actor":"setup","choice":"none","operations":[],"retain":[],"repeat":"no-write"},
33
+ {"state":"yes","actor":"setup","choice":"yes","operations":["reconcile-choice-doc","reconcile-minimal-profile","derive-state","run-foundation-self-test"],"retain":[],"repeat":"no-write"},
34
+ {"state":"later","actor":"setup","choice":"later","operations":["reconcile-choice-doc"],"retain":[],"repeat":"no-write"},
35
+ {"state":"no","actor":"setup","choice":"no","operations":["reconcile-choice-doc","derive-state"],"retain":[],"repeat":"no-write"},
36
+ {"state":"existing","actor":"setup","choice":"recorded","operations":["adopt-choice-doc","derive-state"],"retain":["choice-doc","profile","active","scanner","scanner-test"],"repeat":"no-write"},
37
+ {"state":"explicit-enable","actor":"census-update","choice":"recorded","operations":["delegate-census-update","run-census-update-contract"],"retain":["choice-doc","profile","active","scanner","scanner-test"],"repeat":"no-write"},
38
+ {"state":"disable","actor":"census-update","choice":"recorded","operations":["remove-kit-hook","remove-kit-gate","update-profile-disabled","derive-state"],"retain":["choice-doc","profile-unknown-keys","active","scanner","scanner-test"],"repeat":"no-write"}
39
+ ]
40
+ ```
41
+
42
+ ## Bootstrap profile
43
+
44
+ When `yes` creates the default `.census/profile.json`, write the deterministic
45
+ profile shape documented by `census-update`: `schemaVersion: 1`, `enabled:
46
+ true`, and empty `decisions`, `localScanners`, and `overrides` arrays. Do not
47
+ create `.census/active.json`. Its absence is the evidence that the honest state
48
+ is `bootstrap`, not `current`.
49
+
50
+ Before writing, check for an existing repository convention and adopt it. A
51
+ pre-existing profile remains consumer-owned: preserve unknown keys and do not
52
+ replace its decisions. Never derive `current` from file presence; use
53
+ `resolveCensusState` from `scripts/census/index.mjs`.
54
+
55
+ Run the focused `scripts/census/state.test.mjs` census foundation self-test
56
+ already shipped with the kit.
57
+ A passing self-test proves the mechanism is available, not that this repository
58
+ has been scanned. Setup must not install pre-commit, pre-push, CI, planning, or
59
+ handoff gates for `yes`, `later`, or `no`.
60
+
61
+ ## Later activation and disable
62
+
63
+ `later` and `no` are setup choices, not partially active censuses. A later,
64
+ explicit `census-update` invocation is the sole activation route. Setup
65
+ delegates this route to the shipped `census-update` contract and its focused
66
+ `scripts/test_census_update_contract.test.mjs` proof; it does not reproduce
67
+ activation, snapshots, or enforcement. Setup itself never calls `activateCensus`.
68
+
69
+ Disable follows the ordered contract: remove the kit-owned hook, remove the
70
+ kit-owned gate, then atomically replace only the profile's `enabled` value with
71
+ `false`, preserving unknown keys, and verify `disabled` through
72
+ `resolveCensusState`. Enforcement removal must finish before any profile
73
+ mutation. Treat the choice document, local scanners, their tests, the profile,
74
+ and the active snapshot as consumer-owned evidence. List those files and ask
75
+ for separate deletion approval; without that approval, retain them.
76
+ Setup never deletes consumer-owned files as part of disable.
77
+
78
+ ## Setup report
79
+
80
+ Report the choice, adopted or created paths, derived state, self-test result,
81
+ and whether enforcement changed. Name every action skipped on an idempotent
82
+ rerun. Never claim surface coverage during setup: only `census-update` can
83
+ produce a verified `X of Y` result.
package/README.md CHANGED
@@ -332,6 +332,13 @@ still reference. Flags: `--force` (overwrite pre-existing files on `init`),
332
332
 
333
333
  ## Release notes
334
334
 
335
+ ### 0.16.0
336
+
337
+ - added: `.agents/skills/setup-workflow/census.md`
338
+ - added: `.claude/skills/setup-workflow/census.md`
339
+ - changed: `.agents/skills/setup-workflow/SKILL.md`
340
+ - changed: `.claude/skills/setup-workflow/SKILL.md`
341
+
335
342
  ### 0.15.0
336
343
 
337
344
  - added: `.agents/skills/census-update/SKILL.md`
@@ -1,5 +1,5 @@
1
1
  {
2
- "kitVersion": "0.15.0",
2
+ "kitVersion": "0.16.0",
3
3
  "files": [
4
4
  {
5
5
  "path": ".agents/skills/ask-matt/SKILL.md",
@@ -487,6 +487,15 @@
487
487
  "mode": 420,
488
488
  "origin": "kit"
489
489
  },
490
+ {
491
+ "path": ".agents/skills/setup-workflow/census.md",
492
+ "kind": "skill",
493
+ "ownerSkill": "setup-workflow",
494
+ "surface": "codex",
495
+ "sha256": "ec80f4ad36073e0c7c903434fa098d24f31b9d9a46ce55623782173ddba2e5f9",
496
+ "mode": 420,
497
+ "origin": "kit"
498
+ },
490
499
  {
491
500
  "path": ".agents/skills/setup-workflow/code-review.md",
492
501
  "kind": "skill",
@@ -546,7 +555,7 @@
546
555
  "kind": "skill",
547
556
  "ownerSkill": "setup-workflow",
548
557
  "surface": "codex",
549
- "sha256": "efc7818d98158a85a79f3a652787d2e557552fa5175b32b24916c894388b9b74",
558
+ "sha256": "91ba30cc08206204c83d189274579471366221ccfe8f2abdf36957ad85ae136b",
550
559
  "mode": 420,
551
560
  "origin": "kit"
552
561
  },
@@ -1478,6 +1487,15 @@
1478
1487
  "mode": 420,
1479
1488
  "origin": "kit"
1480
1489
  },
1490
+ {
1491
+ "path": ".claude/skills/setup-workflow/census.md",
1492
+ "kind": "skill",
1493
+ "ownerSkill": "setup-workflow",
1494
+ "surface": "claude",
1495
+ "sha256": "ec80f4ad36073e0c7c903434fa098d24f31b9d9a46ce55623782173ddba2e5f9",
1496
+ "mode": 420,
1497
+ "origin": "kit"
1498
+ },
1481
1499
  {
1482
1500
  "path": ".claude/skills/setup-workflow/code-review.md",
1483
1501
  "kind": "skill",
@@ -1537,7 +1555,7 @@
1537
1555
  "kind": "skill",
1538
1556
  "ownerSkill": "setup-workflow",
1539
1557
  "surface": "claude",
1540
- "sha256": "efc7818d98158a85a79f3a652787d2e557552fa5175b32b24916c894388b9b74",
1558
+ "sha256": "91ba30cc08206204c83d189274579471366221ccfe8f2abdf36957ad85ae136b",
1541
1559
  "mode": 420,
1542
1560
  "origin": "kit"
1543
1561
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikon85/agent-workflow-kit",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Portable AI-agent workflow skills (plan → execute → land → learn) for Claude Code & Codex — grilling, TDD, diagnosis, two-axis code review, cross-model Codex review, design & domain-modeling, plus a skill router (ask-matt). npx init/update/diff/uninstall.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,6 +17,9 @@ Run: python3 scripts/test_skill_setup_workflow_seeds.py
17
17
  """
18
18
  import json
19
19
  import re
20
+ import subprocess
21
+ import tempfile
22
+ import time
20
23
  import unittest
21
24
  from pathlib import Path
22
25
 
@@ -55,6 +58,135 @@ def update_workflow_action(
55
58
  return "create" if prerequisites and pull_requests_allowed else "skip"
56
59
 
57
60
 
61
+ def load_census_setup_effects():
62
+ """Parse the shipped seed's executable census transition contract."""
63
+ seed = (SKILL / "census.md").read_text(encoding="utf-8")
64
+ match = re.search(
65
+ r"```json census-setup-effects\n(.*?)\n```", seed, re.DOTALL,
66
+ )
67
+ if not match:
68
+ raise AssertionError("census seed has no structured setup-effects contract")
69
+ rows = json.loads(match.group(1))
70
+ return {row["state"]: row for row in rows}
71
+
72
+
73
+ def resolve_census_state(**inputs):
74
+ """Ask the shipped #49 API for a state; do not duplicate its state machine."""
75
+ script = (
76
+ "import { resolveCensusState } from './scripts/census/index.mjs';"
77
+ "process.stdout.write(resolveCensusState(JSON.parse(process.argv[1])));"
78
+ )
79
+ result = subprocess.run(
80
+ ["node", "--input-type=module", "-e", script, json.dumps(inputs)],
81
+ cwd=REPO, check=True, capture_output=True, text=True,
82
+ )
83
+ return result.stdout
84
+
85
+
86
+ def run_shipped_test(relative_path):
87
+ """Run a real shipped integration test as proof, without marker files."""
88
+ subprocess.run(
89
+ ["node", "--test", relative_path], cwd=REPO, check=True,
90
+ capture_output=True, text=True,
91
+ )
92
+
93
+
94
+ def apply_census_setup_effect(root, effect, writes):
95
+ """Reconcile setup-owned files and delegate engine work to #49/#50."""
96
+ paths = {
97
+ "choice": root / "docs/agents/census.md",
98
+ "profile": root / ".census/profile.json",
99
+ "active": root / ".census/active.json",
100
+ "scanner": root / ".census/local-scanner.mjs",
101
+ "scanner-test": root / ".census/local-scanner.test.mjs",
102
+ "hook": root / ".git/hooks/census-check",
103
+ "gate": root / ".github/workflows/census-check.yml",
104
+ }
105
+ trace = []
106
+ states = []
107
+
108
+ def write_if_changed(path, content):
109
+ data = content.encode("utf-8")
110
+ if path.exists() and path.read_bytes() == data:
111
+ return
112
+ path.parent.mkdir(parents=True, exist_ok=True)
113
+ path.write_bytes(data)
114
+ writes.append(path.relative_to(root).as_posix())
115
+
116
+ def replace_if_changed(path, content):
117
+ data = content.encode("utf-8")
118
+ if path.exists() and path.read_bytes() == data:
119
+ return
120
+ path.parent.mkdir(parents=True, exist_ok=True)
121
+ staged = path.with_name(path.name + ".setup-workflow.tmp")
122
+ staged.write_bytes(data)
123
+ staged.replace(path)
124
+ writes.append(path.relative_to(root).as_posix())
125
+
126
+ def remove_if_present(path, operation):
127
+ if path.exists():
128
+ path.unlink()
129
+ writes.append(path.relative_to(root).as_posix())
130
+ trace.append(f"{operation}:removed")
131
+ else:
132
+ trace.append(f"{operation}:no-op")
133
+
134
+ for operation in effect["operations"]:
135
+ if operation == "reconcile-choice-doc":
136
+ choice = effect["choice"]
137
+ if not paths["choice"].exists():
138
+ content = (
139
+ "<!-- setup-workflow: state=filled -->\n"
140
+ f"<!-- census: choice={choice} -->\n\n"
141
+ + (SKILL / "census.md").read_text(encoding="utf-8")
142
+ )
143
+ write_if_changed(paths["choice"], content)
144
+ trace.append("reconcile-choice-doc")
145
+ elif operation == "adopt-choice-doc":
146
+ if not paths["choice"].exists():
147
+ raise AssertionError("existing census needs a documented census path")
148
+ trace.append("adopt-choice-doc")
149
+ elif operation == "reconcile-minimal-profile":
150
+ if not paths["profile"].exists():
151
+ write_if_changed(paths["profile"], json.dumps({
152
+ "schemaVersion": 1, "enabled": True,
153
+ "decisions": [], "localScanners": [], "overrides": [],
154
+ }, sort_keys=True) + "\n")
155
+ trace.append("reconcile-minimal-profile")
156
+ elif operation == "remove-kit-hook":
157
+ remove_if_present(paths["hook"], operation)
158
+ elif operation == "remove-kit-gate":
159
+ remove_if_present(paths["gate"], operation)
160
+ elif operation == "update-profile-disabled":
161
+ profile = json.loads(paths["profile"].read_text(encoding="utf-8"))
162
+ profile["enabled"] = False
163
+ replace_if_changed(paths["profile"], json.dumps(profile, sort_keys=True) + "\n")
164
+ trace.append("update-profile-disabled")
165
+ elif operation == "derive-state":
166
+ enabled = False
167
+ if paths["profile"].exists():
168
+ enabled = json.loads(paths["profile"].read_text(encoding="utf-8"))["enabled"]
169
+ states.append(resolve_census_state(
170
+ enabled=enabled, hasActive=paths["active"].exists(),
171
+ ))
172
+ trace.append("derive-state")
173
+ elif operation == "run-foundation-self-test":
174
+ run_shipped_test("scripts/census/state.test.mjs")
175
+ trace.append("run-foundation-self-test")
176
+ elif operation == "delegate-census-update":
177
+ skill = (REPO / ".claude/skills/census-update/SKILL.md").read_text(encoding="utf-8")
178
+ for token in ("activateCensus", "resolveCensusState", "scripts/census/index.mjs"):
179
+ if token not in skill:
180
+ raise AssertionError(f"census-update contract missing {token}")
181
+ trace.append("delegate-census-update")
182
+ elif operation == "run-census-update-contract":
183
+ run_shipped_test("scripts/test_census_update_contract.test.mjs")
184
+ trace.append("run-census-update-contract")
185
+ else:
186
+ raise AssertionError(f"unknown census setup operation: {operation}")
187
+ return paths, trace, states
188
+
189
+
58
190
  class IdempotencyRule(unittest.TestCase):
59
191
  CASES = [
60
192
  # (first_line, is_empty, expected)
@@ -81,6 +213,190 @@ class IdempotencyRule(unittest.TestCase):
81
213
 
82
214
 
83
215
  class SeedTemplatesValid(unittest.TestCase):
216
+ def test_census_effect_contract_executes_every_transition_and_repeats_without_writes(self):
217
+ effects = load_census_setup_effects()
218
+ self.assertEqual(
219
+ set(effects),
220
+ {"missing", "yes", "later", "no", "existing", "explicit-enable", "disable"},
221
+ )
222
+ self.assertEqual(effects["explicit-enable"]["actor"], "census-update")
223
+
224
+ for state, effect in effects.items():
225
+ with self.subTest(state=state), tempfile.TemporaryDirectory() as tmp:
226
+ root = Path(tmp)
227
+ initial = {}
228
+ if state in ("existing", "explicit-enable", "disable"):
229
+ initial = {
230
+ "choice": (
231
+ b'<!-- setup-workflow: state=filled -->\n'
232
+ b'<!-- census: choice=yes -->\n\nconsumer notes stay exact\n'
233
+ ),
234
+ "profile": (
235
+ b'{"enabled":true,"consumerKey":"keep exactly",'
236
+ b'"decisions":["keep"],"overrides":[{"keep":true}]}\n'
237
+ ),
238
+ "active": b'{"consumerSnapshot":"keep exactly"}\n',
239
+ "scanner": b"export const consumerScanner = true;\n",
240
+ "scanner-test": b"consumer scanner test\n",
241
+ }
242
+ if state == "explicit-enable":
243
+ initial["profile"] = b'{"enabled":false,"consumerKey":"keep"}\n'
244
+ initial.pop("active")
245
+ for name, data in initial.items():
246
+ path = {
247
+ "choice": root / "docs/agents/census.md",
248
+ "profile": root / ".census/profile.json",
249
+ "active": root / ".census/active.json",
250
+ "scanner": root / ".census/local-scanner.mjs",
251
+ "scanner-test": root / ".census/local-scanner.test.mjs",
252
+ }[name]
253
+ path.parent.mkdir(parents=True, exist_ok=True)
254
+ path.write_bytes(data)
255
+ if state in ("existing", "disable"):
256
+ for path in (
257
+ root / ".git/hooks/census-check",
258
+ root / ".github/workflows/census-check.yml",
259
+ ):
260
+ path.parent.mkdir(parents=True, exist_ok=True)
261
+ path.write_text("kit-owned census enforcement\n", encoding="utf-8")
262
+
263
+ writes = []
264
+ paths, trace, states = apply_census_setup_effect(root, effect, writes)
265
+
266
+ if state == "missing":
267
+ self.assertEqual(writes, [])
268
+ if state == "yes":
269
+ choice = paths["choice"].read_text(encoding="utf-8")
270
+ self.assertTrue(choice.startswith(
271
+ "<!-- setup-workflow: state=filled -->\n"
272
+ "<!-- census: choice=yes -->\n"
273
+ ))
274
+ profile = json.loads(paths["profile"].read_text(encoding="utf-8"))
275
+ self.assertEqual(profile, {
276
+ "schemaVersion": 1, "enabled": True,
277
+ "decisions": [], "localScanners": [], "overrides": [],
278
+ })
279
+ self.assertEqual(states, ["bootstrap"])
280
+ self.assertIn("run-foundation-self-test", trace)
281
+ if state in ("yes", "later", "no"):
282
+ self.assertFalse(paths["active"].exists())
283
+ self.assertFalse(paths["hook"].exists())
284
+ self.assertFalse(paths["gate"].exists())
285
+ if state in ("later", "no"):
286
+ self.assertFalse(paths["profile"].exists())
287
+ self.assertIn(
288
+ f"<!-- census: choice={state} -->",
289
+ paths["choice"].read_text(encoding="utf-8"),
290
+ )
291
+ if state == "existing":
292
+ self.assertEqual(writes, [])
293
+ for name, expected in initial.items():
294
+ self.assertEqual(paths[name].read_bytes(), expected)
295
+ if state == "explicit-enable":
296
+ self.assertEqual(writes, [])
297
+ for name, expected in initial.items():
298
+ self.assertEqual(paths[name].read_bytes(), expected)
299
+ self.assertEqual(
300
+ trace,
301
+ ["delegate-census-update", "run-census-update-contract"],
302
+ )
303
+ if state == "disable":
304
+ profile = json.loads(paths["profile"].read_text(encoding="utf-8"))
305
+ self.assertFalse(profile["enabled"])
306
+ self.assertEqual(profile["consumerKey"], "keep exactly")
307
+ self.assertEqual(profile["decisions"], ["keep"])
308
+ self.assertEqual(profile["overrides"], [{"keep": True}])
309
+ for name in ("choice", "active", "scanner", "scanner-test"):
310
+ self.assertEqual(paths[name].read_bytes(), initial[name])
311
+ self.assertFalse(paths["hook"].exists())
312
+ self.assertFalse(paths["gate"].exists())
313
+ self.assertEqual(states, ["disabled"])
314
+ self.assertLess(
315
+ next(i for i, value in enumerate(trace) if value.startswith("remove-kit-hook:")),
316
+ trace.index("update-profile-disabled"),
317
+ )
318
+ self.assertLess(
319
+ next(i for i, value in enumerate(trace) if value.startswith("remove-kit-gate:")),
320
+ trace.index("update-profile-disabled"),
321
+ )
322
+
323
+ before = {
324
+ str(path.relative_to(root)): (path.read_bytes(), path.stat().st_mtime_ns)
325
+ for path in root.rglob("*") if path.is_file()
326
+ }
327
+ time.sleep(0.01)
328
+ repeat_writes = []
329
+ apply_census_setup_effect(root, effect, repeat_writes)
330
+ after = {
331
+ str(path.relative_to(root)): (path.read_bytes(), path.stat().st_mtime_ns)
332
+ for path in root.rglob("*") if path.is_file()
333
+ }
334
+ self.assertEqual(effect["repeat"], "no-write")
335
+ self.assertEqual(repeat_writes, [])
336
+ self.assertEqual(after, before)
337
+
338
+ def test_census_seed_covers_the_complete_setup_state_matrix(self):
339
+ seed = (SKILL / "census.md").read_text(encoding="utf-8")
340
+ rows = {}
341
+ for line in seed.splitlines():
342
+ match = re.match(r"^\| `([^`]+)` \| (.+) \| (.+) \|$", line)
343
+ if match:
344
+ rows[match.group(1)] = f"{match.group(2)} {match.group(3)}"
345
+ for state in (
346
+ "missing", "yes", "later", "no", "existing",
347
+ "explicit-enable", "disable",
348
+ ):
349
+ self.assertIn(state, rows)
350
+
351
+ expected_terms = {
352
+ "missing": ("ask `yes / later / no`", "do not infer", "no hook or gate"),
353
+ "yes": ("enabled: true", "active snapshot absent", "self-test", "bootstrap"),
354
+ "later": ("deferral", "setup rerun is a no-op", "`census-update`"),
355
+ "no": ("opt-out", "`disabled`", "do not create census files, hooks, or gates"),
356
+ "existing": ("Adopt", "without replacing", "Preserve every existing byte"),
357
+ "explicit-enable": ("`census-update`", "without rerunning setup", "no write"),
358
+ "disable": ("enabled: false", "remove census hooks/gates", "separately approves"),
359
+ }
360
+ for state, terms in expected_terms.items():
361
+ for term in terms:
362
+ self.assertIn(term, rows[state], f"{state} missing {term!r}")
363
+
364
+ def test_census_yes_is_an_honest_bootstrap_not_activation(self):
365
+ skill = (SKILL / "SKILL.md").read_text(encoding="utf-8")
366
+ seed = (SKILL / "census.md").read_text(encoding="utf-8")
367
+
368
+ self.assertIn("optional census choice", skill.split("---", 2)[1])
369
+ self.assertIn("Section A3 — Optional project census", skill)
370
+ self.assertIn("[census.md](./census.md)", skill)
371
+ for token in (
372
+ ".census/profile.json", ".census/active.json", "enabled: true",
373
+ "bootstrap", "not yet meaningful", "self-test",
374
+ "Setup itself never calls `activateCensus`",
375
+ ):
376
+ self.assertIn(token, seed)
377
+ self.assertIn("must not install pre-commit, pre-push, CI, planning, or", seed)
378
+
379
+ def test_census_deferral_adoption_enable_and_disable_are_safe_and_idempotent(self):
380
+ skill = (SKILL / "SKILL.md").read_text(encoding="utf-8")
381
+ seed = (SKILL / "census.md").read_text(encoding="utf-8")
382
+
383
+ for token in (
384
+ "retryable deferral", "explicit opt-out", "explicit `census-update`",
385
+ "consumer-owned", "separate deletion approval", "no write",
386
+ ):
387
+ self.assertIn(token, seed)
388
+ self.assertIn("Repeated runs are no-ops", skill)
389
+ self.assertIn("setup never deletes consumer-owned files", seed.lower())
390
+
391
+ def test_census_setup_surface_is_fully_mirrored_for_codex(self):
392
+ codex = REPO / ".agents/skills/setup-workflow"
393
+ for relative in ("SKILL.md", "census.md"):
394
+ self.assertEqual(
395
+ (SKILL / relative).read_text(encoding="utf-8"),
396
+ (codex / relative).read_text(encoding="utf-8"),
397
+ f"setup-workflow mirror drift in {relative}",
398
+ )
399
+
84
400
  def test_update_workflow_provider_and_choice_fixtures(self):
85
401
  fixtures = [
86
402
  ("github", "enable", False, True, True, "create"),