@tyroneross/build-loop 0.30.3 → 0.35.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/.agents/plugins/marketplace.json +2 -2
- package/.claude-plugin/marketplace.json +4 -4
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/AGENTS.md +11 -2
- package/README.md +8 -7
- package/agents/advisor.md +111 -0
- package/agents/alignment-checker.md +11 -0
- package/agents/assessment-orchestrator.md +1 -1
- package/agents/build-orchestrator.md +28 -33
- package/agents/fact-checker.md +1 -1
- package/agents/fix-critique.md +1 -1
- package/agents/independent-auditor.md +1 -1
- package/agents/overfitting-reviewer.md +1 -1
- package/agents/plan-critic.md +8 -6
- package/agents/promotion-reviewer.md +1 -1
- package/agents/retrospective-synthesizer.md +20 -3
- package/agents/scope-auditor.md +1 -1
- package/agents/security-reviewer.md +1 -1
- package/agents/self-improvement-architect.md +1 -1
- package/commands/{handoff.md → compose-handoff.md} +4 -4
- package/commands/{optimize.md → optimize-run.md} +1 -1
- package/commands/{agent-rally-point.md → rally-point.md} +3 -3
- package/commands/{research.md → research-run.md} +1 -1
- package/commands/setup-memory.md +32 -0
- package/commands/{plan-verify.md → verify-plan.md} +1 -1
- package/docs/agent-surface-policy.md +38 -20
- package/docs/memory-setup.md +19 -14
- package/hooks/_resolve_python.sh +28 -0
- package/hooks/closeout.sh +58 -0
- package/hooks/git/pre-push +69 -6
- package/hooks/hooks.json +34 -13
- package/hooks/post-push-closeout.sh +73 -0
- package/hooks/session-start-closeout.sh +69 -0
- package/hooks/session-start-memory.sh +27 -0
- package/hooks/session-start-plugin-heal.sh +33 -0
- package/hooks/test_closeout.sh +124 -0
- package/package.json +7 -4
- package/plugin-artifacts/codex/.codex-plugin/plugin.json +41 -0
- package/plugin-artifacts/codex/AGENTS.md +507 -0
- package/plugin-artifacts/codex/BUILD-ARTIFACT.md +5 -0
- package/plugin-artifacts/codex/LICENSE +202 -0
- package/plugin-artifacts/codex/README.md +404 -0
- package/plugin-artifacts/codex/docs/agent-surface-policy.md +63 -0
- package/plugin-artifacts/codex/references/advisor-dispatch-ladder.md +62 -0
- package/plugin-artifacts/codex/references/agent-role-taxonomy.md +135 -0
- package/plugin-artifacts/codex/references/autonomous-and-per-commit-modes.md +161 -0
- package/plugin-artifacts/codex/references/autonomy-config.md +231 -0
- package/plugin-artifacts/codex/references/capability-routing.md +182 -0
- package/plugin-artifacts/codex/references/codex-subagents.md +106 -0
- package/plugin-artifacts/codex/references/coordination-file-template.md +181 -0
- package/plugin-artifacts/codex/references/coordination-rules.md +283 -0
- package/plugin-artifacts/codex/references/dogfood-reload-checkpoint.md +112 -0
- package/plugin-artifacts/codex/references/halt-and-ask-protocol.md +102 -0
- package/plugin-artifacts/codex/references/implementer-envelope-schema.md +302 -0
- package/plugin-artifacts/codex/references/intent-capability-pack.md +257 -0
- package/plugin-artifacts/codex/references/intent-exploration-prompts.md +96 -0
- package/plugin-artifacts/codex/references/leadership.md +72 -0
- package/plugin-artifacts/codex/references/memory-systems.md +238 -0
- package/plugin-artifacts/codex/references/memory.md +299 -0
- package/plugin-artifacts/codex/references/model-tier-mapping.md +257 -0
- package/plugin-artifacts/codex/references/modular-systems-pack.md +96 -0
- package/plugin-artifacts/codex/references/phase-1-assess.md +231 -0
- package/plugin-artifacts/codex/references/phase-2-plan.md +78 -0
- package/plugin-artifacts/codex/references/phase-3-execute.md +49 -0
- package/plugin-artifacts/codex/references/phase-4-review.md +322 -0
- package/plugin-artifacts/codex/references/phase-5-iterate.md +71 -0
- package/plugin-artifacts/codex/references/phase-6-learn.md +56 -0
- package/plugin-artifacts/codex/references/recent-design-structures.md +274 -0
- package/plugin-artifacts/codex/references/research-trigger-policy.md +140 -0
- package/plugin-artifacts/codex/references/runtime-smoke-triggers.md +42 -0
- package/plugin-artifacts/codex/references/self-review.md +234 -0
- package/plugin-artifacts/codex/references/single-writer-commit-protocol.md +90 -0
- package/plugin-artifacts/codex/references/task-capture-policy.md +68 -0
- package/plugin-artifacts/codex/references/ui-io-contract.md +116 -0
- package/plugin-artifacts/codex/references/ui-spotcheck-protocol.md +65 -0
- package/plugin-artifacts/codex/skills/build-loop/SKILL.md +372 -0
- package/plugin-artifacts/codex/skills/build-loop/detect-plugins.mjs +82 -0
- package/plugin-artifacts/codex/skills/build-loop/eval-guide.md +65 -0
- package/plugin-artifacts/codex/skills/build-loop/fallbacks.md +549 -0
- package/plugin-artifacts/codex/skills/build-loop/phases/fact-check.md +42 -0
- package/plugin-artifacts/codex/skills/build-loop/phases/ui-validation.md +267 -0
- package/plugin-artifacts/codex/skills/build-loop/references/advisor-dispatch-ladder.md +62 -0
- package/plugin-artifacts/codex/skills/build-loop/references/apple-native-planning.md +439 -0
- package/plugin-artifacts/codex/skills/build-loop/references/autonomous-and-per-commit-modes.md +161 -0
- package/plugin-artifacts/codex/skills/build-loop/references/capability-routing.md +182 -0
- package/plugin-artifacts/codex/skills/build-loop/references/codex-subagents.md +106 -0
- package/plugin-artifacts/codex/skills/build-loop/references/coordination.md +161 -0
- package/plugin-artifacts/codex/skills/build-loop/references/correction-aware-capture.md +177 -0
- package/plugin-artifacts/codex/skills/build-loop/references/independent-auditor.md +72 -0
- package/plugin-artifacts/codex/skills/build-loop/references/intent-capability-pack.md +257 -0
- package/plugin-artifacts/codex/skills/build-loop/references/intent-exploration-prompts.md +96 -0
- package/plugin-artifacts/codex/skills/build-loop/references/leadership.md +72 -0
- package/plugin-artifacts/codex/skills/build-loop/references/memory.md +299 -0
- package/plugin-artifacts/codex/skills/build-loop/references/modular-systems-pack.md +96 -0
- package/plugin-artifacts/codex/skills/build-loop/references/output-style.md +129 -0
- package/plugin-artifacts/codex/skills/build-loop/references/pay-it-forward-arch.md +98 -0
- package/plugin-artifacts/codex/skills/build-loop/references/phase-1-assess.md +231 -0
- package/plugin-artifacts/codex/skills/build-loop/references/phase-2-plan.md +78 -0
- package/plugin-artifacts/codex/skills/build-loop/references/phase-3-execute.md +49 -0
- package/plugin-artifacts/codex/skills/build-loop/references/phase-4-review.md +322 -0
- package/plugin-artifacts/codex/skills/build-loop/references/phase-5-iterate.md +71 -0
- package/plugin-artifacts/codex/skills/build-loop/references/phase-6-learn.md +56 -0
- package/plugin-artifacts/codex/skills/build-loop/references/recent-design-structures.md +274 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/ASSESSMENT.md +85 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/STANDALONE_TEST_RUN.md +149 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/01-simple-bugfix.md +32 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/02-ui-build-with-iteration.md +48 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/03-multi-failure-escalation.md +60 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/04-ui-build-ibr-absent.md +51 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/05-refactor-navgator-absent.md +71 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/06-recurring-bug-debugger-absent.md +52 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/trace-comparison.md +202 -0
- package/plugin-artifacts/codex/skills/build-loop/references/self-recursive-dev.md +77 -0
- package/plugin-artifacts/codex/skills/build-loop/references/self-review.md +234 -0
- package/plugin-artifacts/codex/skills/build-loop/references/ui-io-contract.md +116 -0
- package/plugin-artifacts/codex/skills/build-loop/scanners/audit-design-rules.mjs +476 -0
- package/plugin-artifacts/codex/skills/build-loop/scanners/require-visual-evidence.mjs +239 -0
- package/plugin-artifacts/codex/skills/build-loop/templates/backlog-item.md +35 -0
- package/plugin-artifacts/codex/skills/build-loop/templates/codex-worker-prompt.md +100 -0
- package/plugin-artifacts/codex/skills/build-loop/templates/ui-subagent-prompt.md +179 -0
- package/plugin-artifacts/codex/skills/build-loop/templates/ux-fix-plan.md +40 -0
- package/scripts/_paths.py +59 -13
- package/scripts/build_codex_plugin_artifact.py +314 -0
- package/scripts/check_cache_sync.py +1 -1
- package/scripts/install_memory.py +11 -3
- package/scripts/prune_plugin_cache.py +225 -11
- package/scripts/sync_navgator_lessons.py +25 -0
- package/scripts/sync_plugin_cache.py +66 -36
- package/skills/agent-rally-point/SKILL.md +1 -1
- package/skills/agent-rally-watcher/SKILL.md +1 -1
- package/skills/build-loop/SKILL.md +11 -155
- package/skills/build-loop/references/advisor-dispatch-ladder.md +62 -0
- package/skills/build-loop/references/autonomous-and-per-commit-modes.md +161 -0
- package/skills/build-loop/references/capability-routing.md +4 -3
- package/skills/build-loop/references/codex-subagents.md +8 -0
- package/skills/build-loop/references/coordination.md +1 -1
- package/skills/build-loop/references/intent-capability-pack.md +88 -1
- package/skills/build-loop/references/memory.md +17 -2
- package/skills/build-loop/references/phase-1-assess.md +13 -0
- package/skills/build-loop/references/phase-2-plan.md +2 -1
- package/skills/build-loop/references/phase-3-execute.md +4 -2
- package/skills/build-loop/references/phase-4-review.md +12 -0
- package/skills/build-loop/references/phase-6-learn.md +1 -1
- package/skills/build-loop/references/self-review.md +4 -2
- package/skills/build-loop/templates/codex-worker-prompt.md +45 -34
- package/skills/handoff/SKILL.md +3 -3
- package/skills/loop-builder/SKILL.md +98 -0
- package/skills/loop-builder/presets/active-project-evidence.yaml +97 -0
- package/skills/loop-builder/presets/generic-artifact-loop.yaml +95 -0
- package/skills/loop-builder/presets/presentation-audit.yaml +96 -0
- package/skills/loop-builder/presets/research-synthesis.yaml +96 -0
- package/skills/loop-builder/presets/source-ingestion-raw-data-audit.yaml +96 -0
- package/skills/loop-builder/references/spec-format.md +80 -0
- package/skills/loop-builder/scripts/loop_builder.py +346 -0
- package/skills/model-tiering/SKILL.md +65 -40
- package/skills/optimize/SKILL.md +2 -2
- package/skills/plan-verify/SKILL.md +1 -1
- package/skills/plugin-builder/references/distribution.md +8 -0
- package/skills/research/SKILL.md +33 -6
- package/skills/runtime-parity-verification/SKILL.md +51 -0
- package/skills/spec-writing/SKILL.md +19 -0
- package/templates/memory/README.md +65 -0
- package/templates/memory/charter.md.template +46 -0
- package/templates/memory/manifest.json +24 -4
- package/codex-skills/debug-loop/SKILL.md +0 -18
- package/codex-skills/handoff/SKILL.md +0 -22
- package/codex-skills/knowledge/SKILL.md +0 -21
- package/codex-skills/optimize/SKILL.md +0 -18
- package/codex-skills/research/SKILL.md +0 -18
- /package/commands/{knowledge-review.md → review-knowledge.md} +0 -0
package/agents/plan-critic.md
CHANGED
|
@@ -18,7 +18,7 @@ description: |
|
|
|
18
18
|
user: "Run plan-critic on the proposed plan"
|
|
19
19
|
assistant: "I'll dispatch plan-critic to surface scope-split overlaps, missing alternatives, and headline drift."
|
|
20
20
|
</example>
|
|
21
|
-
model:
|
|
21
|
+
model: fable
|
|
22
22
|
color: purple
|
|
23
23
|
tools: ["Read", "Grep", "Glob"]
|
|
24
24
|
---
|
|
@@ -34,9 +34,10 @@ You are an adversarial plan critic. You have no ability to fix files — only to
|
|
|
34
34
|
|
|
35
35
|
## Severity policy
|
|
36
36
|
|
|
37
|
-
- All your findings cap at **WARN
|
|
38
|
-
- Only `plan_verify.py` emits BLOCKERs.
|
|
39
|
-
- The orchestrator
|
|
37
|
+
- All your **emitted** findings cap at **WARN** — you report problems, you never decide whether the run halts (single-judge bias is documented; the gate is the orchestrator's, not yours).
|
|
38
|
+
- Only `plan_verify.py` emits deterministic BLOCKERs. Your findings are reasoning-level.
|
|
39
|
+
- **The orchestrator gates on your WARNs conditionally:** on high-stakes plans (`synthesisDensity > 5`, `triggers.riskSurfaceChange`, `stakes >= medium`, or `dispatch_tier: frontier`) your WARNs are treated as **blocking** — Phase 2 does not finish until each is revised or explicitly overridden. On all other plans they stay **advisory** (today's behavior). This is the cheapest way to put a Frontier verdict on the high-stakes plan path before any implementer runs.
|
|
40
|
+
- Ground every finding in an **objective signal** — *what in the plan* fails, with a `file:line` and the rule it violates — never "I'm unsure about this." Self-reported confidence is never a gate trigger; overconfidence is documented.
|
|
40
41
|
|
|
41
42
|
## What to flag
|
|
42
43
|
|
|
@@ -47,6 +48,7 @@ You are an adversarial plan critic. You have no ability to fix files — only to
|
|
|
47
48
|
| **MECE scope** | Phase splits / file ownership splits are mutually exclusive and collectively exhaustive. Flag overlapping owners (same file in two phases) and unowned responsibilities (required behavior with no phase). |
|
|
48
49
|
| **Headline drift** | Section headlines align with the stated intent across the doc. Flag when a section's claims contradict its own header or the plan's top-level goal. |
|
|
49
50
|
| **Verification depth** | Factual assertions about repo state (callers, imports, package presence) cite a specific verification command or path — not just "verified". |
|
|
51
|
+
| **Activation Map adequacy** | When the plan proposes a new event-driven / call-site component but has NO `## Activation Map`, WARN even if `plan_verify` stayed silent (its keyword heuristic has bounded recall — you are the recall backstop). When the plan has an `## Activation Map` (required for new event-driven / call-site components — `plan_verify` enforces the section's presence and that each entry carries `trigger:` + `verified-live:`), grade whether each `trigger:` names a *real host event or call site*, not an aspiration. Inadequate: "runs at review time", "fires when needed", "on the relevant event". Adequate: "PostToolUse:Bash matcher in hooks/hooks.json", "SessionStart hook", "pre-commit hook in .pre-commit-config.yaml". WARN on any vague trigger, and WARN on any `verified-live: pending` entry that has no corresponding verification task before Report. |
|
|
50
52
|
|
|
51
53
|
## Required output shape
|
|
52
54
|
|
|
@@ -55,7 +57,7 @@ Emit a list of findings, each conforming to the Plan Evidence Contract used by `
|
|
|
55
57
|
```json
|
|
56
58
|
{
|
|
57
59
|
"claim_text": "...",
|
|
58
|
-
"claim_kind": "less_invasive_shim|marker_adequacy|scope_mece|headline_drift|verification_depth",
|
|
60
|
+
"claim_kind": "less_invasive_shim|marker_adequacy|scope_mece|headline_drift|verification_depth|activation_map_adequacy",
|
|
59
61
|
"subject": {"path": null, "symbol": null, "noun": null},
|
|
60
62
|
"verification_command": null,
|
|
61
63
|
"evidence": {"file": "<plan-path>", "line": 42, "snippet": "..."},
|
|
@@ -63,7 +65,7 @@ Emit a list of findings, each conforming to the Plan Evidence Contract used by `
|
|
|
63
65
|
"marker": null,
|
|
64
66
|
"severity": "WARN",
|
|
65
67
|
"confidence": "low|medium|high",
|
|
66
|
-
"rule_id": "alternatives-considered|marker-adequacy|scope-mece|headline-drift|verification-depth"
|
|
68
|
+
"rule_id": "alternatives-considered|marker-adequacy|scope-mece|headline-drift|verification-depth|activation-map-adequacy"
|
|
67
69
|
}
|
|
68
70
|
```
|
|
69
71
|
|
|
@@ -14,7 +14,7 @@ description: |
|
|
|
14
14
|
user: "Promote this experiment"
|
|
15
15
|
assistant: "I'll run promotion-reviewer first to surface any policy variances, then present the verdict + diff for user confirmation."
|
|
16
16
|
</example>
|
|
17
|
-
model:
|
|
17
|
+
model: fable
|
|
18
18
|
color: purple
|
|
19
19
|
tools: ["Read", "Grep", "Glob"]
|
|
20
20
|
---
|
|
@@ -65,9 +65,23 @@ The Python pipeline produces deterministic bullets from captured signals. When y
|
|
|
65
65
|
|
|
66
66
|
If you do enrich, re-write the active file using `Edit` (preserving the headers; only adding new bullets under existing section headers). Skip enrichment when the deterministic output already captures everything.
|
|
67
67
|
|
|
68
|
-
## Step 3 —
|
|
68
|
+
## Step 3 — Emit closeout status (mandatory)
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
After Step 2, run the machine-readable closeout — this is the durable enforcement layer for the build-loop memory closeout contract:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
python3 -m closeout \
|
|
74
|
+
--workdir "$WORKDIR" \
|
|
75
|
+
--run-id "$RUN_ID" \
|
|
76
|
+
--source post-push \
|
|
77
|
+
--json
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The script emits exactly one `closeout_status`: `wrote_memory` | `queued_pending_lesson` | `no_durable_lesson`. Copy it into your envelope under `closeout_status` and `closeout_reason`. The script is non-raising; on degraded internal error it returns exit 0 with `error:` populated — surface that under `closeout_error` and continue. A skipped closeout on a run with durable signal is a DETECTABLE failure (asserted by `scripts/closeout/test_status.py`), so this step is non-optional.
|
|
81
|
+
|
|
82
|
+
## Step 4 — Return envelope
|
|
83
|
+
|
|
84
|
+
Return the JSON envelope verbatim from Step 1 (plus an `enrichment_applied: true|false` flag if you modified the file in Step 2, plus `closeout_status` / `closeout_reason` / `closeout_error` from Step 3). Example shape:
|
|
71
85
|
|
|
72
86
|
```json
|
|
73
87
|
{
|
|
@@ -78,7 +92,10 @@ Return the JSON envelope verbatim from Step 1 (plus an `enrichment_applied: true
|
|
|
78
92
|
"status": "ok",
|
|
79
93
|
"reason": null,
|
|
80
94
|
"meta": { "run_id": "...", "prompt_count": 24, "cluster_count": 2, "transcript_present": true },
|
|
81
|
-
"enrichment_applied": false
|
|
95
|
+
"enrichment_applied": false,
|
|
96
|
+
"closeout_status": "wrote_memory | queued_pending_lesson | no_durable_lesson",
|
|
97
|
+
"closeout_reason": "human-readable reason",
|
|
98
|
+
"closeout_error": null
|
|
82
99
|
}
|
|
83
100
|
```
|
|
84
101
|
|
package/agents/scope-auditor.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: scope-auditor
|
|
3
3
|
description: Read-only Plan→Execute boundary check. For every commit that changes a public function/component/type signature, traces every caller-site outside the commit's owned-files, then either confirms `internal_only: true` or appends the missing caller files to the appropriate commit's owned-files list. Prevents the "fan-out scope-blindness" defect class observed in round-2 of dispatch-pattern testing (example-app 2026-05-07).
|
|
4
|
-
model:
|
|
4
|
+
model: fable
|
|
5
5
|
tools: ["Read", "Grep", "Glob"]
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -14,7 +14,7 @@ description: |
|
|
|
14
14
|
user: "Security check on the auth changes"
|
|
15
15
|
assistant: "I'll use the security-reviewer agent — diff vs OWASP Web A01/A03 + LLM06 + ASI03 — and emit a structured findings report."
|
|
16
16
|
</example>
|
|
17
|
-
model:
|
|
17
|
+
model: fable
|
|
18
18
|
color: red
|
|
19
19
|
tools: ["Read", "Grep", "Glob"]
|
|
20
20
|
---
|
|
@@ -115,7 +115,7 @@ Keep the Experiment section small otherwise: one metric, one decision rule, expl
|
|
|
115
115
|
## Constraints
|
|
116
116
|
|
|
117
117
|
- **No invention**. If the pattern evidence is weak, flag it in your synthesis output (`warning: low-signal pattern, artifact is speculative`) and produce a minimal skill.
|
|
118
|
-
- **No promotion authority**. You draft, never promote. Promotion is
|
|
118
|
+
- **No promotion authority**. You draft, never promote. Promotion verdict is `promotion-reviewer` (Fable) territory, dispatched via build-orchestrator.
|
|
119
119
|
- **No changes outside `.build-loop/`**. Experimental artifacts are project-local.
|
|
120
120
|
- **Include a removal pointer**. The user must be able to delete your artifact with one command.
|
|
121
121
|
- **Use `plugin-dev` skills**. Do not wing the SKILL.md format. Load the reference.
|
|
@@ -79,7 +79,7 @@ Present the full output. Also note:
|
|
|
79
79
|
|
|
80
80
|
To launch a fresh session after handoff, re-run with `--launch`:
|
|
81
81
|
```
|
|
82
|
-
/build-loop:handoff --launch
|
|
82
|
+
/build-loop:compose-handoff --launch
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
{{/if}}
|
|
@@ -90,18 +90,18 @@ To launch a fresh session after handoff, re-run with `--launch`:
|
|
|
90
90
|
|
|
91
91
|
**Emit to stdout (default)**
|
|
92
92
|
```
|
|
93
|
-
/build-loop:handoff
|
|
93
|
+
/build-loop:compose-handoff
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
**Write to file**
|
|
97
97
|
```
|
|
98
|
-
/build-loop:handoff --workdir /path/to/repo
|
|
98
|
+
/build-loop:compose-handoff --workdir /path/to/repo
|
|
99
99
|
```
|
|
100
100
|
(Use `--output` flag in the script directly for file output.)
|
|
101
101
|
|
|
102
102
|
**Emit + launch fresh session**
|
|
103
103
|
```
|
|
104
|
-
/build-loop:handoff --launch
|
|
104
|
+
/build-loop:compose-handoff --launch
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
**JSON envelope (for programmatic use)**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: optimize
|
|
2
|
+
name: optimize-run
|
|
3
3
|
description: "(Advanced override — `/build-loop:run` auto-routes here; use this only to force the mode.) Run autonomous metric-driven optimization. Detects available targets or accepts a specific one."
|
|
4
4
|
argument-hint: "[target]"
|
|
5
5
|
---
|
|
@@ -178,7 +178,7 @@ python3 ${CLAUDE_PLUGIN_ROOT}/scripts/coordination_bootstrap.py \
|
|
|
178
178
|
--json
|
|
179
179
|
```
|
|
180
180
|
|
|
181
|
-
Example invocation: `/
|
|
181
|
+
Example invocation: `/build-loop:rally-point init v0130-feature-x "Add new feature X across orchestrator + bootstrap + tests"`
|
|
182
182
|
|
|
183
183
|
Example output:
|
|
184
184
|
|
|
@@ -220,7 +220,7 @@ python3 ${CLAUDE_PLUGIN_ROOT}/scripts/agent_rally.py lead <op> \
|
|
|
220
220
|
- `relinquish` — give up the lead so the next `claim` succeeds immediately.
|
|
221
221
|
- `status` — read the current lead + `lease_valid`.
|
|
222
222
|
|
|
223
|
-
Example: `/
|
|
223
|
+
Example: `/build-loop:rally-point lead status`
|
|
224
224
|
|
|
225
225
|
### `escalate <reason>`
|
|
226
226
|
|
|
@@ -269,7 +269,7 @@ Shows the subcommand reference table (replaces what the no-args case used to do
|
|
|
269
269
|
Outputs:
|
|
270
270
|
|
|
271
271
|
```
|
|
272
|
-
/
|
|
272
|
+
/build-loop:rally-point — inspect or invoke build-loop's multi-session coordination
|
|
273
273
|
|
|
274
274
|
Subcommands:
|
|
275
275
|
status (default) Sensor poll: active peers, unresolved verdicts, coord file
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: research
|
|
2
|
+
name: research-run
|
|
3
3
|
description: "(Advanced override — `/build-loop:run` auto-routes here; use this only to force the mode.) Generate a repo-grounded research packet without committing to build. Pre-decision analysis."
|
|
4
4
|
argument-hint: "[topic]"
|
|
5
5
|
---
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Set up or verify the build-loop central memory store (build-loop-memory). Guided and idempotent — copies only the packaged public seed, never overwrites your content. Safe to re-run."
|
|
3
|
+
argument-hint: "[--check]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com> | SPDX-License-Identifier: Apache-2.0 -->
|
|
7
|
+
|
|
8
|
+
{{#if ARGUMENTS}}
|
|
9
|
+
{{#contains ARGUMENTS "--check"}}
|
|
10
|
+
Run `python3 "${CLAUDE_PLUGIN_ROOT:-$PWD}/scripts/install_memory.py" --check` and report the memory store status: which template files and which lanes (`indexes/`, `projects/`) exist vs are missing. Do not write anything.
|
|
11
|
+
{{else}}
|
|
12
|
+
Run `python3 "${CLAUDE_PLUGIN_ROOT:-$PWD}/scripts/install_memory.py" --guided` and report what was seeded vs already present, then surface the installer's printed next steps to the user.
|
|
13
|
+
{{/contains}}
|
|
14
|
+
{{else}}
|
|
15
|
+
Run `python3 "${CLAUDE_PLUGIN_ROOT:-$PWD}/scripts/install_memory.py" --guided` and report what was seeded vs already present, then surface the installer's printed next steps to the user.
|
|
16
|
+
{{/if}}
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Reference
|
|
21
|
+
|
|
22
|
+
**Guided setup (idempotent — public seed only, no personal content):**
|
|
23
|
+
```
|
|
24
|
+
/build-loop:setup-memory
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Status check (no writes):**
|
|
28
|
+
```
|
|
29
|
+
/build-loop:setup-memory --check
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The store lives at `memory_store_root()`. Resolution: an env override (`$BUILD_LOOP_MEMORY_STORE_ROOT` / `$BUILD_LOOP_MEMORY_ROOT` / `$AGENT_MEMORY_ROOT`), else a pre-existing legacy `~/dev/git-folder/build-loop-memory` if it is already on disk, else the neutral fresh-install default `~/.build-loop-memory`. On a fresh machine it is also bootstrapped automatically on session start by `hooks/session-start-memory.sh` when entirely absent; this command is the manual / repair path and the way to see what's present. Research packets, project lessons, decisions, and debugging memory all persist under this store.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: plan
|
|
2
|
+
name: verify-plan
|
|
3
3
|
description: "Deterministic verifier for plan markdown — grep-checkable rules (delete-with-callers, numeric-drift, route-change-evidence, package-state, missing-evidence). Run before accepting a Phase 2 plan."
|
|
4
4
|
argument-hint: "<plan.md>"
|
|
5
5
|
---
|
|
@@ -1,25 +1,42 @@
|
|
|
1
1
|
# Agent Surface Policy
|
|
2
2
|
|
|
3
|
-
Build Loop
|
|
4
|
-
helpers
|
|
3
|
+
Build Loop keeps one user-facing Codex entrypoint while preserving the richer
|
|
4
|
+
Claude Code skill tree for hosts that can route helpers internally.
|
|
5
5
|
|
|
6
6
|
## Public Entry Points
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Codex must expose exactly one user-facing Build Loop choice:
|
|
9
9
|
|
|
10
|
-
- `build-loop` — main
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
- `build-loop` — main workflow and router for build, fix, debug, optimize,
|
|
11
|
+
research, knowledge capture, handoff, and plugin-test work
|
|
12
|
+
|
|
13
|
+
Claude Code may expose advanced direct overrides in its command/skill surface
|
|
14
|
+
where useful:
|
|
15
|
+
|
|
16
|
+
- `build-loop`
|
|
17
|
+
- `debug-loop`
|
|
18
|
+
- `optimize`
|
|
19
|
+
- `research`
|
|
20
|
+
- `knowledge`
|
|
21
|
+
- `handoff`
|
|
16
22
|
|
|
17
23
|
## Host Rules
|
|
18
24
|
|
|
19
|
-
Codex and ChatGPT
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
internal
|
|
25
|
+
Codex and ChatGPT install from `plugin-artifacts/codex`, not from the source
|
|
26
|
+
repo root. Codex auto-discovers root `skills/*/SKILL.md` files and manifest
|
|
27
|
+
skill paths supplement that default, so installing the source root leaks every
|
|
28
|
+
internal helper into the picker. The artifact is generated by:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
python3 scripts/build_codex_plugin_artifact.py --source . --target plugin-artifacts/codex
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The artifact contains one `skills/build-loop/SKILL.md` copied from the
|
|
35
|
+
canonical workflow and `.codex-plugin/plugin.json` rewritten to
|
|
36
|
+
`"skills": "./skills"`. It must not contain any other `SKILL.md` files.
|
|
37
|
+
|
|
38
|
+
The local Codex marketplace (`.agents/plugins/marketplace.json`) points at this
|
|
39
|
+
artifact. Release and marketplace updates must do the same.
|
|
23
40
|
|
|
24
41
|
Claude Code keeps `.claude-plugin/plugin.json` pointed at `./skills` because
|
|
25
42
|
commands and orchestrator agents load internal skills by qualified name. Helper
|
|
@@ -27,19 +44,20 @@ skills must set `user-invocable: false`; the public entrypoints set
|
|
|
27
44
|
`user-invocable: true`.
|
|
28
45
|
|
|
29
46
|
Cursor and other AGENTS.md-style tools should treat `AGENTS.md` plus this file
|
|
30
|
-
as the routing contract. Start from
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
as the routing contract. Start from `build-loop`. Read helper files under
|
|
48
|
+
`skills/` only when the active entrypoint, a command, or an orchestrator
|
|
49
|
+
instruction explicitly references them.
|
|
33
50
|
|
|
34
51
|
## Cache Hygiene
|
|
35
52
|
|
|
36
53
|
Plugin cache pruning is explicit and auditable:
|
|
37
54
|
|
|
38
55
|
```bash
|
|
56
|
+
npm run codex:sync-cache
|
|
39
57
|
python3 scripts/prune_plugin_cache.py --source . --apply
|
|
40
58
|
```
|
|
41
59
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
and
|
|
45
|
-
`--host claude` for a single host.
|
|
60
|
+
`npm run codex:sync-cache` rebuilds the slim artifact first, then syncs that
|
|
61
|
+
artifact into the Codex cache. The prune command keeps the current host manifest
|
|
62
|
+
version and deletes older verified cache directories for the same plugin. Use
|
|
63
|
+
`--host codex` or `--host claude` for a single host.
|
package/docs/memory-setup.md
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
# Memory Setup
|
|
2
2
|
|
|
3
3
|
Build-loop's advisory judges and the Phase 1 Assess memory-load step read from
|
|
4
|
-
one consolidated tree
|
|
4
|
+
one consolidated tree. The root (`<memory-root>` below) is resolved by
|
|
5
|
+
`scripts/_paths.memory_store_root()`:
|
|
6
|
+
|
|
7
|
+
1. an env override — `$BUILD_LOOP_MEMORY_STORE_ROOT` / `$BUILD_LOOP_MEMORY_ROOT` / `$AGENT_MEMORY_ROOT`;
|
|
8
|
+
2. else a pre-existing legacy `~/dev/git-folder/build-loop-memory` if it is already on disk (installs that predate the neutral default keep their location with zero config);
|
|
9
|
+
3. else the neutral fresh-install default `~/.build-loop-memory`.
|
|
5
10
|
|
|
6
11
|
| Tier | Location | Owner | Versioning |
|
|
7
12
|
|---|---|---|---|
|
|
8
|
-
| Global |
|
|
9
|
-
| Project |
|
|
13
|
+
| Global | `<memory-root>/` plus top-level lanes such as `lessons/` | This user | **Should be in a private git repo** (your lessons live here) |
|
|
14
|
+
| Project | `<memory-root>/projects/<slug>/` (slug derived via `scripts/_paths.derive_slug_from_cwd` — basename of the git repo root, lowercased + normalized; `workers/` sub-component becomes `<slug>/workers`) | This user | Same private repo as global |
|
|
10
15
|
|
|
11
16
|
> **History** — until PR 3 of the memory-consolidation series (merged 2026-05-13), the legacy per-repo location was also read by `memory_facade._resolve_memory_dirs` as a transitional shim. As of PR 3, only the consolidated tree is read; any pre-migration content still at the legacy path is invisible. Operators with such content should run `scripts/migrate_project_memory.py --apply` (idempotent), then `scripts/cleanup_legacy_memory_stubs.py --apply` to remove the now-inert `.MOVED.md` stubs.
|
|
12
17
|
|
|
@@ -20,10 +25,10 @@ operator preferences that are not appropriate for public distribution.
|
|
|
20
25
|
|
|
21
26
|
```bash
|
|
22
27
|
# Guided terminal install. This validates the packaged public seed first, then
|
|
23
|
-
# creates
|
|
28
|
+
# creates <memory-root>/ with scaffold-only files.
|
|
24
29
|
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/install_memory.py --guided
|
|
25
30
|
|
|
26
|
-
# Bootstrap with templates (creates
|
|
31
|
+
# Bootstrap with templates (creates <memory-root>/ if missing,
|
|
27
32
|
# seeds constitution.md + MEMORY.md from templates/memory/, plus indexes/ and projects/)
|
|
28
33
|
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/install_memory.py
|
|
29
34
|
|
|
@@ -37,7 +42,7 @@ python3 ${CLAUDE_PLUGIN_ROOT}/scripts/install_memory.py --validate-seed
|
|
|
37
42
|
This creates:
|
|
38
43
|
|
|
39
44
|
```
|
|
40
|
-
|
|
45
|
+
<memory-root>/
|
|
41
46
|
├── constitution.md # template — replace with your invariants
|
|
42
47
|
├── MEMORY.md # template — index for entries you add
|
|
43
48
|
├── indexes/ # rebuildable local indexes, including semantic_facts.sqlite
|
|
@@ -79,7 +84,7 @@ The repo should contain `constitution.md` and `MEMORY.md` plus your existing les
|
|
|
79
84
|
|
|
80
85
|
```bash
|
|
81
86
|
python3 scripts/install_memory.py
|
|
82
|
-
cd
|
|
87
|
+
cd <memory-root> # default ~/.build-loop-memory on a fresh install
|
|
83
88
|
git init
|
|
84
89
|
git add constitution.md MEMORY.md
|
|
85
90
|
git commit -m "init: build-loop memory scaffolding"
|
|
@@ -150,7 +155,7 @@ Keep `MEMORY.md` under ~200 lines; entries past that get truncated when loaded i
|
|
|
150
155
|
## What about decisions?
|
|
151
156
|
|
|
152
157
|
The **canonical decision store** is
|
|
153
|
-
|
|
158
|
+
`<memory-root>/projects/<project>/decisions/`. It is
|
|
154
159
|
project-tagged and written by `scripts/write_decision.py` — it captures the
|
|
155
160
|
discrete "we decided X" events of any project.
|
|
156
161
|
|
|
@@ -158,11 +163,11 @@ The top-level `lessons/` lane is for **cross-project lessons** — patterns and
|
|
|
158
163
|
feedback that apply broadly, not project-specific decisions.
|
|
159
164
|
|
|
160
165
|
```
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
+
<memory-root>/ # canonical root
|
|
167
|
+
<memory-root>/lessons/ # cross-project lessons
|
|
168
|
+
<memory-root>/indexes/updates.jsonl # global update ledger
|
|
169
|
+
<memory-root>/projects/<slug>/ # project-local memory
|
|
170
|
+
<memory-root>/projects/_archive/<slug>/ # retired projects, still queryable
|
|
166
171
|
<repo>/.build-loop/memory/ # legacy project location — no longer read (PR 3 removed the shim); migrate via scripts/migrate_project_memory.py
|
|
167
172
|
<repo>/.episodic/decisions/ # legacy local decision store (migration/archive)
|
|
168
173
|
```
|
|
@@ -215,7 +220,7 @@ If any of these fail, see `references/memory-systems.md` for the full backend to
|
|
|
215
220
|
|
|
216
221
|
## Privacy
|
|
217
222
|
|
|
218
|
-
Treat
|
|
223
|
+
Treat `<memory-root>/` as containing potentially-sensitive context:
|
|
219
224
|
|
|
220
225
|
- Operator preferences, project-specific decisions, client names if you write them in
|
|
221
226
|
- Constitution rules may reveal architectural patterns from past projects
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com>
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
#
|
|
5
|
+
# _resolve_python.sh — source (don't exec) to set `$_py` to a usable python3
|
|
6
|
+
# without depending on a populated PATH. Hooks run under a minimal PATH
|
|
7
|
+
# (/usr/bin:/bin), so resolve via `command -v` then absolute fallbacks. Sets
|
|
8
|
+
# `_py=""` when none is found.
|
|
9
|
+
#
|
|
10
|
+
# . "$(dirname "$0")/_resolve_python.sh"
|
|
11
|
+
# [ -n "$_py" ] || exit 0
|
|
12
|
+
#
|
|
13
|
+
# Single source of truth for the resolver shared by closeout.sh,
|
|
14
|
+
# session-start-closeout.sh, and post-push-closeout.sh. See memory
|
|
15
|
+
# `reference_hooks_minimal_path_failopen`.
|
|
16
|
+
|
|
17
|
+
_py=""
|
|
18
|
+
for _candidate in python3 python; do
|
|
19
|
+
if command -v "$_candidate" >/dev/null 2>&1; then
|
|
20
|
+
_py="$_candidate"
|
|
21
|
+
break
|
|
22
|
+
fi
|
|
23
|
+
done
|
|
24
|
+
for _fallback in /usr/bin/python3 /usr/local/bin/python3 /opt/homebrew/bin/python3; do
|
|
25
|
+
if [ -z "$_py" ] && [ -x "$_fallback" ]; then
|
|
26
|
+
_py="$_fallback"
|
|
27
|
+
fi
|
|
28
|
+
done
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com>
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
#
|
|
5
|
+
# closeout.sh — structural run-close shim for INLINE build-loop runs (f6).
|
|
6
|
+
#
|
|
7
|
+
# Backs the Claude Code `Stop` hook and the Codex `Stop` equivalent (see
|
|
8
|
+
# hooks/hooks.json + .codex/hooks.json). Thin minimal-PATH-safe wrapper around
|
|
9
|
+
# scripts/stop_closeout.py, which holds the (tested) logic. Mirrors the existing
|
|
10
|
+
# `commit_state_check.py --hook` invocation pattern.
|
|
11
|
+
#
|
|
12
|
+
# $1 = mode: "stop" (default) | "session-start"
|
|
13
|
+
#
|
|
14
|
+
# Contract:
|
|
15
|
+
# - Advisory + fail-open: ALWAYS exit 0; never `decision: block`.
|
|
16
|
+
# - Self-gates on `.build-loop/` presence (walk up); silent elsewhere — safe
|
|
17
|
+
# to install globally.
|
|
18
|
+
# - Minimal-PATH safe: hooks run under /usr/bin:/bin. Resolve python3 via
|
|
19
|
+
# `command -v` + absolute fallbacks; missing → silent exit 0.
|
|
20
|
+
# - stdin (the hook JSON payload, carrying session_id) is forwarded to python
|
|
21
|
+
# untouched so stop_closeout.py can read the session id.
|
|
22
|
+
#
|
|
23
|
+
# See memory `reference_hooks_minimal_path_failopen`.
|
|
24
|
+
|
|
25
|
+
set -u
|
|
26
|
+
|
|
27
|
+
MODE="${1:-stop}"
|
|
28
|
+
|
|
29
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-${PWD}}"
|
|
30
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-${PROJECT_DIR}}"
|
|
31
|
+
|
|
32
|
+
# Walk up from the project dir to find a `.build-loop/`. Absent → silent exit 0
|
|
33
|
+
# (this hook is safe to install globally; it does nothing outside a build-loop repo).
|
|
34
|
+
_dir="$PROJECT_DIR"
|
|
35
|
+
_found=""
|
|
36
|
+
for _ in 1 2 3 4 5 6 7 8; do
|
|
37
|
+
if [ -d "${_dir}/.build-loop" ]; then
|
|
38
|
+
_found="$_dir"
|
|
39
|
+
break
|
|
40
|
+
fi
|
|
41
|
+
_parent="$(dirname "$_dir")"
|
|
42
|
+
[ "$_parent" = "$_dir" ] && break
|
|
43
|
+
_dir="$_parent"
|
|
44
|
+
done
|
|
45
|
+
[ -n "$_found" ] || exit 0
|
|
46
|
+
|
|
47
|
+
# Resolve a python3 binary without depending on a populated PATH (shared helper).
|
|
48
|
+
_HOOK_DIR="$(dirname "$0")"
|
|
49
|
+
_py=""
|
|
50
|
+
[ -f "${_HOOK_DIR}/_resolve_python.sh" ] && . "${_HOOK_DIR}/_resolve_python.sh"
|
|
51
|
+
[ -n "$_py" ] || exit 0
|
|
52
|
+
|
|
53
|
+
SCRIPT="${PLUGIN_ROOT}/scripts/stop_closeout.py"
|
|
54
|
+
[ -f "$SCRIPT" ] || exit 0
|
|
55
|
+
|
|
56
|
+
# Emit whatever the helper prints (valid hook JSON), then exit 0 no matter what.
|
|
57
|
+
"$_py" "$SCRIPT" --workdir "$_found" --mode "$MODE" --hook 2>/dev/null || printf '{}'
|
|
58
|
+
exit 0
|
package/hooks/git/pre-push
CHANGED
|
@@ -65,11 +65,18 @@ def _repo_root() -> Path:
|
|
|
65
65
|
return Path.cwd()
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def _fail_open(reason: str) -> int:
|
|
69
|
-
"""Internal-error path: print to stderr, return 0 (allow push).
|
|
68
|
+
def _fail_open(reason: str, *, repo: Path | None = None) -> int:
|
|
69
|
+
"""Internal-error path: print to stderr, return 0 (allow push).
|
|
70
|
+
|
|
71
|
+
Even on the internal-error fail-open path we still ARM the post-push
|
|
72
|
+
closeout baton: a missing ``push_hold`` import shouldn't silently drop
|
|
73
|
+
the durable-memory contract for ad-hoc pushes.
|
|
74
|
+
"""
|
|
70
75
|
sys.stderr.write(
|
|
71
76
|
f"[build-loop pre-push] internal error — allowing push: {reason}\n"
|
|
72
77
|
)
|
|
78
|
+
if repo is not None:
|
|
79
|
+
_arm_post_push_closeout(repo)
|
|
73
80
|
return 0
|
|
74
81
|
|
|
75
82
|
|
|
@@ -110,7 +117,59 @@ def _format_block_message(verdict: dict) -> str:
|
|
|
110
117
|
)
|
|
111
118
|
|
|
112
119
|
|
|
120
|
+
def _arm_post_push_closeout(repo: Path) -> None:
|
|
121
|
+
"""Drop a tiny JSON baton at ``.build-loop/closeout/armed.json``.
|
|
122
|
+
|
|
123
|
+
Git has no native client-side post-push hook. We arm a baton here so the
|
|
124
|
+
next session-start handler (``hooks/session-start-closeout.sh``) drains it
|
|
125
|
+
by invoking ``python3 -m closeout --source post-push-armed``.
|
|
126
|
+
|
|
127
|
+
Fail-open by design — any error is silently swallowed; an armed baton is a
|
|
128
|
+
nice-to-have, never a push blocker.
|
|
129
|
+
"""
|
|
130
|
+
try:
|
|
131
|
+
from datetime import datetime, timezone
|
|
132
|
+
import json as _json
|
|
133
|
+
import subprocess as _sp
|
|
134
|
+
import tempfile as _tf
|
|
135
|
+
|
|
136
|
+
bl_dir = repo / ".build-loop" / "closeout"
|
|
137
|
+
bl_dir.mkdir(parents=True, exist_ok=True)
|
|
138
|
+
head = ""
|
|
139
|
+
try:
|
|
140
|
+
head = _sp.check_output(
|
|
141
|
+
["git", "rev-parse", "HEAD"],
|
|
142
|
+
cwd=str(repo),
|
|
143
|
+
stderr=_sp.DEVNULL,
|
|
144
|
+
text=True,
|
|
145
|
+
).strip()
|
|
146
|
+
except (_sp.CalledProcessError, FileNotFoundError, OSError):
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
payload = {
|
|
150
|
+
"armed_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
151
|
+
"source": "post-push-armed",
|
|
152
|
+
"head_sha": head or None,
|
|
153
|
+
}
|
|
154
|
+
target = bl_dir / "armed.json"
|
|
155
|
+
fd, tmp = _tf.mkstemp(dir=str(bl_dir), prefix=".armed-", suffix=".json")
|
|
156
|
+
try:
|
|
157
|
+
with os.fdopen(fd, "w", encoding="utf-8") as fh:
|
|
158
|
+
_json.dump(payload, fh, indent=2, sort_keys=True)
|
|
159
|
+
fh.write("\n")
|
|
160
|
+
os.replace(tmp, target)
|
|
161
|
+
finally:
|
|
162
|
+
if os.path.exists(tmp):
|
|
163
|
+
try:
|
|
164
|
+
os.unlink(tmp)
|
|
165
|
+
except OSError:
|
|
166
|
+
pass
|
|
167
|
+
except Exception: # noqa: BLE001 — never break a push for the baton
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
|
|
113
171
|
def main() -> int:
|
|
172
|
+
repo: Path | None = None
|
|
114
173
|
try:
|
|
115
174
|
repo = _repo_root()
|
|
116
175
|
scripts_dir = repo / "scripts"
|
|
@@ -119,22 +178,26 @@ def main() -> int:
|
|
|
119
178
|
try:
|
|
120
179
|
import push_hold # type: ignore
|
|
121
180
|
except Exception as exc: # pragma: no cover — exercised by installer test
|
|
122
|
-
return _fail_open(f"could not import push_hold: {exc!r}")
|
|
181
|
+
return _fail_open(f"could not import push_hold: {exc!r}", repo=repo)
|
|
123
182
|
|
|
124
183
|
try:
|
|
125
184
|
stdin_lines = sys.stdin.readlines()
|
|
126
185
|
except OSError as exc:
|
|
127
|
-
return _fail_open(f"could not read stdin: {exc!r}")
|
|
186
|
+
return _fail_open(f"could not read stdin: {exc!r}", repo=repo)
|
|
128
187
|
|
|
129
188
|
verdict = push_hold.evaluate_push(repo, stdin_lines, env=os.environ)
|
|
130
189
|
action = verdict.get("action", "allow")
|
|
131
190
|
if action == "block":
|
|
132
191
|
sys.stderr.write(_format_block_message(verdict))
|
|
133
192
|
return int(verdict.get("exit_code") or 1)
|
|
134
|
-
# allow / bypass / anything else → exit 0
|
|
193
|
+
# allow / bypass / anything else → exit 0.
|
|
194
|
+
# Arm the post-push closeout baton BEFORE the push completes — git has
|
|
195
|
+
# no native client-side post-push hook, so the next session-start
|
|
196
|
+
# drains the baton via ``python3 -m closeout --source post-push-armed``.
|
|
197
|
+
_arm_post_push_closeout(repo)
|
|
135
198
|
return 0
|
|
136
199
|
except Exception as exc: # noqa: BLE001 — broad on purpose; we MUST fail-open
|
|
137
|
-
return _fail_open(f"unhandled exception: {exc!r}")
|
|
200
|
+
return _fail_open(f"unhandled exception: {exc!r}", repo=repo)
|
|
138
201
|
|
|
139
202
|
|
|
140
203
|
if __name__ == "__main__":
|