@chrono-meta/fh-gate 1.4.72 → 1.4.74
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/.claude/rules/.public-surface-patterns.defaults +44 -0
- package/.claude/rules/fh_4axis_gate.md +207 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +26 -2
- package/CATALOG.md +31 -0
- package/docs/ETHOS.md +106 -0
- package/docs/OUTPUT_EVIDENCE.md +118 -0
- package/docs/WHY.md +42 -0
- package/knowledge/patterns/ensemble_union_detection_task_pattern.md +125 -0
- package/knowledge/shared/GLOSSARY.md +77 -0
- package/knowledge/shared/harness-core/measurement-integrity-checklist.md +10 -0
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +554 -0
- package/knowledge/shared/patterns/multi-persona-review.md +88 -0
- package/knowledge/shared/plugin-catalog/recommended_plugins.md +117 -0
- package/package.json +42 -1
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/.claude-plugin/plugin.json +2 -2
- package/plugins/fh-meta/CHANGELOG.md +617 -0
- package/plugins/fh-meta/skills/context-doctor/SKILL.md +42 -4
- package/plugins/fh-meta/skills/context-doctor/SKILL_detail.md +38 -0
- package/scripts/below_floor_scan.sh +91 -0
- package/scripts/chamber_candidate_collect.sh +223 -0
- package/scripts/chamber_run.sh +184 -0
- package/scripts/degrade_direction_scan.sh +222 -0
- package/scripts/fh_env_delta_scan.sh +108 -0
- package/scripts/fh_session_load.sh +202 -0
- package/scripts/gate_pathspec_check.sh +166 -0
- package/scripts/package_coverage_check.sh +119 -0
- package/scripts/prepush_guard_check.sh +374 -0
- package/scripts/psa_scan_lib.sh +153 -0
- package/scripts/public_surface_scan_files.sh +157 -0
- package/scripts/selfcheck.sh +28 -0
- package/scripts/session_close_check.sh +171 -0
- package/scripts/substrate_jump_detector.sh +60 -0
- package/scripts/test_degrade_scan_shell_probes.sh +185 -0
- package/scripts/test_marker_floor_lanes.sh +45 -0
- package/scripts/test_prepush_stdin_integrity.sh +119 -0
- package/scripts/tier_census_grep.sh +54 -0
- package/scripts/universal_guard_check.sh +280 -0
- package/templates/.claude/rules/mcp_tool_gating.md +157 -0
- package/templates/.claude/rules/session.md +153 -0
- package/templates/.git-hooks/pre-commit +848 -0
- package/templates/.git-hooks/pre-push +585 -0
- package/templates/PRE-PUBLISH-CHECKLIST.md +85 -0
- package/templates/contrib_session.md +34 -0
- package/templates/degrade_direction_scan.sh +222 -0
- package/templates/goal-quench-hook-setup.md +152 -0
- package/templates/predelete_check.sh +72 -0
- package/templates/regression_guard.sh +563 -0
- package/templates/starter_profile.md +83 -0
- package/templates/temper_check.sh +46 -0
|
@@ -20,7 +20,7 @@ Diagnoses the main causes of session token waste and prescribes immediate remedi
|
|
|
20
20
|
3. Not using `/clear` after direction changes → continuing work with accumulated noise
|
|
21
21
|
4. Verbose CLI output → every `git`/`ls`/build/test call floods context with stdout (a *different* layer from 1–3 — see §Command-Output Reduction)
|
|
22
22
|
|
|
23
|
-
**Two reduction axes — keep them distinct.** Causes 1–3 are the **context-footprint** axis (what gets read *into* context: files, history). Cause 4 is the **command-output** axis (tokens produced *by* the tools you run). `.claudeignore` cannot touch command output, and a command-output proxy cannot touch file reads — they are complementary, not substitutes.
|
|
23
|
+
**Two reduction axes — keep them distinct.** Causes 1–3 are the **context-footprint** axis (what gets read *into* context: files, history). Cause 4 is the **command-output** axis (tokens produced *by* the tools you run). `.claudeignore` cannot touch command output, and a command-output proxy cannot touch file reads — they are complementary, not substitutes. (Step 3.5 below adds an optional **third**, orthogonal lever — provider-side cache billing — distinct from both.)
|
|
24
24
|
|
|
25
25
|
**Standalone install** — this skill works normally with plugin install only, without cloning the full meta-harness.
|
|
26
26
|
|
|
@@ -111,6 +111,39 @@ When context is near the limit and you want to *preserve state* rather than rese
|
|
|
111
111
|
|
|
112
112
|
> Models are tools for allocating the right expertise to complexity. Opus for simple tasks = wasted expertise; Haiku for design decisions = insufficient expertise. Switch models when task nature changes.
|
|
113
113
|
|
|
114
|
+
### Step 3.5. Cache-Boundary Audit (prompt caching, optional third lever)
|
|
115
|
+
|
|
116
|
+
Distinct from both axes above: Steps 1–3 reduce what occupies the context *window*; this
|
|
117
|
+
reduces what the provider re-bills as **fresh input tokens** on each turn by preserving
|
|
118
|
+
prompt-cache hits. **Optional** — run it when a metered/quota-limited backend makes cache
|
|
119
|
+
cost the binding constraint (same gating logic as §Command-Output Reduction: diagnose and
|
|
120
|
+
recommend, do not chase this when tokens are merely plentiful).
|
|
121
|
+
|
|
122
|
+
**Audit checklist**:
|
|
123
|
+
- Is the system prompt / CLAUDE.md content **stable byte-for-byte** across turns in a
|
|
124
|
+
session? Any turn-to-turn diff in the fixed prefix invalidates the cache for that prefix —
|
|
125
|
+
which is in tension with Step 5's CLAUDE.md-compression prescription: compress at session
|
|
126
|
+
boundaries (before a fresh cache is built), never mid-session (which would invalidate one
|
|
127
|
+
already warm).
|
|
128
|
+
- Is session-specific / dynamic content (task state, recent results) placed at the
|
|
129
|
+
**end of the user message**, not interleaved into the system prompt or early context?
|
|
130
|
+
|
|
131
|
+
**Reported, UNCALIBRATED by FH** (2026-07-27 frontier digest, issue #102): input:output token
|
|
132
|
+
ratio **> 10:1** favors cache/context engineering over model-level optimization; **> 50:1**,
|
|
133
|
+
prefix caching dominates; cache-boundary control reportedly raised hit rate ~7%→84% in one
|
|
134
|
+
production case (arXiv:2603.09619 *Context Engineering: From Prompts to Corporate Multi-Agent
|
|
135
|
+
Architecture* · appscale.blog).
|
|
136
|
+
|
|
137
|
+
**What is and is not verified** — the two are separate checks and only one has been run:
|
|
138
|
+
the cited paper's **existence** was confirmed 2026-07-28 (arxiv.org/abs/2603.09619 → HTTP 200,
|
|
139
|
+
title matches; measured alongside a known-real control ID, so the check itself is calibrated).
|
|
140
|
+
The **figures** above were not read out of the paper — they are still traced only to the digest
|
|
141
|
+
comment. Treat the ratio thresholds as illustrative, not a calibrated gate, until the source
|
|
142
|
+
text is read directly.
|
|
143
|
+
|
|
144
|
+
> **Detail**: See `SKILL_detail.md §CacheBoundary` — full citation text and the 403 grounding
|
|
145
|
+
> note — read before citing these figures elsewhere.
|
|
146
|
+
|
|
114
147
|
### Step 4. harvest-loop Integration (burst pattern recording)
|
|
115
148
|
|
|
116
149
|
When burst pattern is detected and `tracks/_audit/` exists: locate the latest weekly_audit file and suggest adding the Token Efficiency Check items to it (bash + checklist block in §Step-Bash).
|
|
@@ -132,6 +165,8 @@ Run the audit bash (§Step-Bash) and apply thresholds:
|
|
|
132
165
|
|
|
133
166
|
**Frequency**: When explicitly called with `/context-doctor` or auto-invoked at session start when MEMORY.md is detected at 180+ lines.
|
|
134
167
|
|
|
168
|
+
**Cache-boundary note**: apply CLAUDE.md compression (above) at a session boundary, not mid-session — see Step 3.5's cache-invalidation tension if that step is also in scope.
|
|
169
|
+
|
|
135
170
|
## Context Hierarchy (L1/L2/L3)
|
|
136
171
|
|
|
137
172
|
Information buried in the middle of a long context window suffers measurable accuracy loss — the "lost in the middle" effect, ~10–30% degradation for mid-context information (see `../../../../knowledge/shared/harness-core/harness_frontier_diagnosis_2026-06-02.md`). The remedy is two-fold: tier the context, and place the most important instructions at the **start and end**, never buried in the middle.
|
|
@@ -187,8 +222,8 @@ The reductions above all act on the **context-footprint** axis (files, history r
|
|
|
187
222
|
|
|
188
223
|
| Environment | Behavior |
|
|
189
224
|
|---|---|
|
|
190
|
-
| Meta-harness cloned (Mode A) | Perform full Steps 1–5 / integrate with harvest-loop files |
|
|
191
|
-
| Plugin only (Mode C) | Perform Steps 1–3 / Step 4 output only (no file writes) |
|
|
225
|
+
| Meta-harness cloned (Mode A) | Perform full Steps 1–5 (+ optional Step 3.5) / integrate with harvest-loop files |
|
|
226
|
+
| Plugin only (Mode C) | Perform Steps 1–3 (+ optional Step 3.5, no file writes needed) / Step 4 output only (no file writes) |
|
|
192
227
|
| External general environment | Focus on `.claudeignore` generation + large file guidance |
|
|
193
228
|
|
|
194
229
|
## Invocation Triggers
|
|
@@ -231,6 +266,7 @@ Explicit invocation (`/context-doctor`) always runs regardless of suppress state
|
|
|
231
266
|
- "context diet", "memory audit", "CLAUDE.md is heavy", "MEMORY.md size"
|
|
232
267
|
- "context engineering", "context rot", "context collapse"
|
|
233
268
|
- "command output is huge", "verbose output", "rtk", "token killer", "trim command output"
|
|
269
|
+
- "prompt caching", "cache hit rate", "cache boundary", "why are my API costs so high"
|
|
234
270
|
|
|
235
271
|
### Natural Language Triggers (activates without internal vocabulary)
|
|
236
272
|
|
|
@@ -247,6 +283,7 @@ Also activates when an external user expresses without token/context terminology
|
|
|
247
283
|
| "Context is getting full", "context meter is high" | Approaching context limit | Step 3 — propose Wrap-then-Compact pattern |
|
|
248
284
|
| "context engineering", "doing context engineering", "context rot setting in" | 2026 industry term for context discipline (Chroma 2025 / Anthropic) | Step 2 + Step 3 |
|
|
249
285
|
| "every git command dumps a wall of text", "the build output eats my context" | Verbose command output flooding context | §Command-Output Reduction (route to proxy/hook) |
|
|
286
|
+
| "our token bill is high but context looks fine", "cache keeps missing" | Suspected cache-boundary invalidation, not context bloat | Step 3.5 (optional) |
|
|
250
287
|
|
|
251
288
|
## Three-Doctor Loop Integration
|
|
252
289
|
|
|
@@ -277,9 +314,10 @@ context-doctor (token/context) · harness-doctor (structure) · sim-conductor (s
|
|
|
277
314
|
|
|
278
315
|
| Condition | Completion verdict |
|
|
279
316
|
|---|---|
|
|
280
|
-
| Diagnosis results output to conversation (relevant stages among Steps 1~5) | ✅ Diagnosis complete |
|
|
317
|
+
| Diagnosis results output to conversation (relevant stages among Steps 1~5, incl. optional 3.5) | ✅ Diagnosis complete |
|
|
281
318
|
| `.claudeignore` created or modified + path output | ✅ Prescription complete |
|
|
282
319
|
| Large file detected with split strategy guidance output | ✅ Step 2 complete |
|
|
320
|
+
| Step 3.5 run: checklist findings output, thresholds labeled UNCALIBRATED per source above | ✅ Cache-boundary audit complete (only when Step 3.5 was triggered) |
|
|
283
321
|
| "No context structure issues" judgment output | ✅ Health check complete |
|
|
284
322
|
|
|
285
323
|
**This skill's Done When = "diagnosis report output complete"**. Actual resolution of prescription items is in the user's or follow-up work domain and is not included in this skill's completion criteria.
|
|
@@ -93,6 +93,44 @@ CC Context Audit Results
|
|
|
93
93
|
|
|
94
94
|
---
|
|
95
95
|
|
|
96
|
+
## §CacheBoundary — prompt-caching evidence (Step 3.5, provisional)
|
|
97
|
+
|
|
98
|
+
Full citation for the Step 3.5 evidence line, kept out of the always-read SKILL.md body
|
|
99
|
+
per this file's own role (detail loads on-demand, SKILL.md stays lean).
|
|
100
|
+
|
|
101
|
+
**Source**: forge-harness GitHub issue #102 ("Frontier Digest Log"), 2026-07-27 daily comment,
|
|
102
|
+
itself citing:
|
|
103
|
+
- arXiv:2603.09619 — reported input:output token-ratio thresholds (>10:1 favors context/cache
|
|
104
|
+
engineering over model-level optimization; >50:1, prefix caching dominates) and a
|
|
105
|
+
41–80% cost reduction / 13–31% TTFT improvement range attributed to prompt caching.
|
|
106
|
+
- appscale.blog/en/blog/context-engineering-production-llm-agents-token-budget-compaction-2026
|
|
107
|
+
— the ~7%→84% cache-hit-rate figure from cache-boundary control (fixed system prompt,
|
|
108
|
+
dynamic content at the user-message end).
|
|
109
|
+
|
|
110
|
+
**Grounding status — split verdict (2026-07-28 re-check).** Two separate questions; only one
|
|
111
|
+
is closed. Do not collapse them:
|
|
112
|
+
|
|
113
|
+
| Question | Verdict | Evidence |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| Does the cited paper exist? | ✅ **VERIFIED** | `arxiv.org/abs/2603.09619` → HTTP 200, title *"Context Engineering: From Prompts to Corporate Multi-Agent Architecture"*. Measured alongside a known-real control ID (1706.03762 → 200), so the instrument is calibrated for this check. |
|
|
116
|
+
| Do the paper/blog actually state these figures? | ❌ **UNVERIFIED** | The source text was never read. The numbers are traced only to the digest comment. |
|
|
117
|
+
|
|
118
|
+
⚠️ **Instrument note (kept because it nearly produced a wrong verdict).** The first re-check
|
|
119
|
+
used the arXiv **API** (`export.arxiv.org/api/query`) and returned empty for the target — but
|
|
120
|
+
also empty for the known-real control, i.e. the instrument was dead, not the paper missing.
|
|
121
|
+
An absence measured without a live control is not evidence. Switching to the `abs` page gave a
|
|
122
|
+
working control and flipped the existence verdict to VERIFIED. Original 403 note below for
|
|
123
|
+
provenance.
|
|
124
|
+
|
|
125
|
+
**Original (2026-07-27) note**: both URLs returned HTTP 403 when that session attempted
|
|
126
|
+
to fetch and verify the claimed spans directly (`WebFetch`), matching the same sandbox
|
|
127
|
+
network-policy limitation the daily frontier-digest routine logged on every run this week
|
|
128
|
+
(WebSearch fallback, no direct API access). The figures above are therefore traced only to
|
|
129
|
+
the digest comment's own text — a real, read GitHub source — and are **not** independently
|
|
130
|
+
re-verified against the original paper/post. Per the Instrument-Calibration doctrine
|
|
131
|
+
(`CLAUDE.md`), do not treat the 10:1 / 50:1 thresholds as a calibrated gate and do not cite
|
|
132
|
+
the ~7%→84% figure elsewhere without re-fetching from an environment with access first.
|
|
133
|
+
|
|
96
134
|
## §Headroom — Tooling (external option)
|
|
97
135
|
|
|
98
136
|
The compression pass in SKILL.md is tool-agnostic, but a concrete, reversible, local-first implementation exists: **Headroom** (`github.com/chopratejas/headroom`, open source, v0.22). It compresses tool outputs, logs, files, and RAG chunks before they reach the LLM — **vendor/coverage-reported** at 60–95% fewer tokens with the same answers ([The Register, 2026-05-31](https://www.theregister.com/ai-ml/2026/05/31/netflix-wiz-creates-app-to-slash-ai-bills-then-open-sources-it/5248702); figures unverified by FH). General token-efficiency basis: `../../../../knowledge/shared/harness-core/harness_frontier_diagnosis_2026-06-02.md`.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# below_floor_scan.sh — standing consumer for below-floor adversarial markers
|
|
3
|
+
#
|
|
4
|
+
# The pre-commit hook accepts a below-floor Axis-2 pass when an operator ack is
|
|
5
|
+
# present, on the promise that "the weekly audit re-queues below-floor markers
|
|
6
|
+
# for floor-tier re-run" (§Floor governance, multi_model_sidecar_strategy.md).
|
|
7
|
+
# This script IS that consumer: it enumerates below-floor markers and reports
|
|
8
|
+
# which ones still await floor-tier re-validation. Read-only, zero side effects.
|
|
9
|
+
#
|
|
10
|
+
# Resolution protocol (append to the marker after acting — machine-greppable):
|
|
11
|
+
# floor-rerun: <YYYY-MM-DD> <model> PASS|FAIL ← Axis 2 re-run at >= floor
|
|
12
|
+
# floor-writeoff: <YYYY-MM-DD> <one-line reason> ← operator writes the ack off
|
|
13
|
+
#
|
|
14
|
+
# Usage: bash scripts/below_floor_scan.sh [repo_root]
|
|
15
|
+
# Exit: 0 = no pending below-floor markers; 1 = pending re-runs found
|
|
16
|
+
# (manual weekly-audit step — Phase 1.5; exit 1 = raise the pending
|
|
17
|
+
# items as S-tier. No automated caller is wired yet.)
|
|
18
|
+
|
|
19
|
+
set -uo pipefail
|
|
20
|
+
|
|
21
|
+
REPO_ROOT="${1:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
|
|
22
|
+
MARKER_DIR="$REPO_ROOT/tracks/_meta"
|
|
23
|
+
PENDING=0
|
|
24
|
+
TOTAL=0
|
|
25
|
+
|
|
26
|
+
echo "── below-floor marker scan: $MARKER_DIR ──"
|
|
27
|
+
|
|
28
|
+
if [ ! -d "$MARKER_DIR" ]; then
|
|
29
|
+
echo " (no tracks/_meta directory — nothing to scan)"
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
SONNET_PENDING=0
|
|
34
|
+
for m in "$MARKER_DIR"/.axes_23_passed_*.marker; do
|
|
35
|
+
[ -e "$m" ] || continue
|
|
36
|
+
# sonnet-floor lane (Sonnet-Floor Doctrine 2026-07-10): anchored Sonnet passes are
|
|
37
|
+
# first-class at commit time but provisional for judged depth — queued here as
|
|
38
|
+
# R-tier (advisory re-validation), distinct from below-floor's S-tier hard queue.
|
|
39
|
+
if grep -qE '^[[:space:]]*floor-status:[[:space:]]*sonnet-floor' "$m"; then
|
|
40
|
+
name=$(basename "$m")
|
|
41
|
+
rerun=$(grep -m1 -E '^[[:space:]]*floor-rerun:' "$m" \
|
|
42
|
+
| sed -E 's/^[[:space:]]*floor-rerun:[[:space:]]*//' || true)
|
|
43
|
+
writeoff=$(grep -m1 -E '^[[:space:]]*floor-writeoff:' "$m" \
|
|
44
|
+
| sed -E 's/^[[:space:]]*floor-writeoff:[[:space:]]*//' || true)
|
|
45
|
+
if [ -n "$rerun" ]; then
|
|
46
|
+
echo "✅ RESOLVED (re-run) $name — floor-rerun: $rerun"
|
|
47
|
+
elif [ -n "$writeoff" ]; then
|
|
48
|
+
echo "✅ RESOLVED (writeoff) $name — floor-writeoff: $writeoff"
|
|
49
|
+
else
|
|
50
|
+
SONNET_PENDING=$((SONNET_PENDING + 1))
|
|
51
|
+
anchor=$(grep -m1 -E '^[[:space:]]*axis2-anchor:' "$m" \
|
|
52
|
+
| sed -E 's/^[[:space:]]*axis2-anchor:[[:space:]]*//' || true)
|
|
53
|
+
echo "🟨 R-tier re-validate $name (sonnet-floor)"
|
|
54
|
+
echo " axis2-anchor: ${anchor:-<missing>}"
|
|
55
|
+
echo " action: re-run judged depth at >= opus or via sidecar dispatch when"
|
|
56
|
+
echo " available, append 'floor-rerun: ...' — or 'floor-writeoff: ...'"
|
|
57
|
+
fi
|
|
58
|
+
continue
|
|
59
|
+
fi
|
|
60
|
+
grep -qE '^[[:space:]]*floor-status:[[:space:]]*below-floor' "$m" || continue
|
|
61
|
+
TOTAL=$((TOTAL + 1))
|
|
62
|
+
name=$(basename "$m")
|
|
63
|
+
model=$(grep -m1 -E '^[[:space:]]*axis2-model:' "$m" \
|
|
64
|
+
| sed -E 's/^[[:space:]]*axis2-model:[[:space:]]*//' || true)
|
|
65
|
+
ack=$(grep -m1 -E '^[[:space:]]*below-floor-ack:' "$m" \
|
|
66
|
+
| sed -E 's/^[[:space:]]*below-floor-ack:[[:space:]]*//' || true)
|
|
67
|
+
rerun=$(grep -m1 -E '^[[:space:]]*floor-rerun:' "$m" \
|
|
68
|
+
| sed -E 's/^[[:space:]]*floor-rerun:[[:space:]]*//' || true)
|
|
69
|
+
writeoff=$(grep -m1 -E '^[[:space:]]*floor-writeoff:' "$m" \
|
|
70
|
+
| sed -E 's/^[[:space:]]*floor-writeoff:[[:space:]]*//' || true)
|
|
71
|
+
|
|
72
|
+
if [ -n "$rerun" ]; then
|
|
73
|
+
echo "✅ RESOLVED (re-run) $name — floor-rerun: $rerun"
|
|
74
|
+
elif [ -n "$writeoff" ]; then
|
|
75
|
+
echo "✅ RESOLVED (writeoff) $name — floor-writeoff: $writeoff"
|
|
76
|
+
else
|
|
77
|
+
PENDING=$((PENDING + 1))
|
|
78
|
+
echo "🟥 PENDING re-run $name"
|
|
79
|
+
echo " axis2-model: ${model:-<missing>} | ack: ${ack:-<missing>}"
|
|
80
|
+
echo " action: re-run Axis 2 at >= floor (opus), append 'floor-rerun: ...'"
|
|
81
|
+
echo " or operator writes off: append 'floor-writeoff: ...'"
|
|
82
|
+
fi
|
|
83
|
+
done
|
|
84
|
+
|
|
85
|
+
echo "── below-floor markers: $TOTAL total, $PENDING pending (S-tier) ──"
|
|
86
|
+
echo "── sonnet-floor markers pending re-validation: $SONNET_PENDING (R-tier, advisory) ──"
|
|
87
|
+
# Exit semantics unchanged: only below-floor (S-tier) hard-fails the scan.
|
|
88
|
+
# sonnet-floor pendings are advisory — reported, never exit-blocking (doctrine:
|
|
89
|
+
# Sonnet base is first-class; the queue exists so residuals terminate, not decorate).
|
|
90
|
+
[ "$PENDING" -gt 0 ] && exit 1
|
|
91
|
+
exit 0
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# chamber_candidate_collect.sh — chamber candidate discovery: 6-source converge → dedup → rank → screen.
|
|
3
|
+
#
|
|
4
|
+
# The second half of the discovery entrance (the first half = chamber_candidate_screen.sh, the reinvention
|
|
5
|
+
# first-pass). Design home: tracks/_meta/fh_signal_2026-07-14_self-dev.md (design v2, 2-family verified).
|
|
6
|
+
#
|
|
7
|
+
# WHAT IT DOES: pulls candidate signals from the 6 sources, deduplicates the same gap arriving multiple
|
|
8
|
+
# ways (2-family finding: harvest-loop/fh_signal/UAP observe the SAME session → triple-listing), ranks by
|
|
9
|
+
# source-diversity + frequency, and runs each survivor through the reinvention screener. Output = a ranked
|
|
10
|
+
# candidate queue for the operator's HITL "run the chamber?" decision (judged uncertainty/failure-cost
|
|
11
|
+
# filters stay with the human — this tool does the mechanizable converge/dedup/rank/screen).
|
|
12
|
+
#
|
|
13
|
+
# CANDIDATE-EMISSION CONVENTION (why a convention, not source-specific parsing): the 2-family review
|
|
14
|
+
# flagged per-source parsing as fragile/hand-wave. So a source declares a candidate with ONE robust line:
|
|
15
|
+
# CHAMBER-CANDIDATE: <one-line description of the capability/project to incubate>
|
|
16
|
+
# Any of the 6 sources adds this line when it surfaces something chamber-worthy. Sources adopt it
|
|
17
|
+
# incrementally — until they do, the queue is honestly SPARSE (the design's "6-source volume = n=0,
|
|
18
|
+
# measured only after wiring"; this tool IS that wiring, and its first run measures the volume).
|
|
19
|
+
#
|
|
20
|
+
# NOT MECHANIZED HERE (stays judged/HITL, by design): uncertainty + failure-cost filters (operator judges
|
|
21
|
+
# the ranked survivors); chamber injection (goal-quench budget gate + HITL). This tool ranks; it does not
|
|
22
|
+
# admit. A high rank is a suggestion, never an auto-run.
|
|
23
|
+
#
|
|
24
|
+
# Usage: bash scripts/chamber_candidate_collect.sh [--min-jaccard N] (default 50)
|
|
25
|
+
# exit 0 = ran (ranked queue on stdout; empty queue is a valid honest result)
|
|
26
|
+
# exit 2 = harness error (FH root not found)
|
|
27
|
+
|
|
28
|
+
set -uo pipefail
|
|
29
|
+
|
|
30
|
+
FH="$(cd "$(dirname "$0")/.." && pwd)"
|
|
31
|
+
if [ ! -d "$FH/tracks/_meta" ] || [ ! -d "$FH/plugins" ]; then
|
|
32
|
+
echo "❌ FH root not found at '$FH' — run from the FH repo." >&2
|
|
33
|
+
exit 2
|
|
34
|
+
fi
|
|
35
|
+
SCREEN="$FH/scripts/chamber_candidate_screen.sh"
|
|
36
|
+
MINJ=50
|
|
37
|
+
[ "${1:-}" = "--min-jaccard" ] && MINJ="${2:-50}"
|
|
38
|
+
case "$MINJ" in ''|*[!0-9]*) echo "❌ --min-jaccard must be an integer 0-100 (got '$MINJ')" >&2; exit 2 ;; esac
|
|
39
|
+
|
|
40
|
+
WORK="${TMPDIR:-/tmp}/cand_collect.$$"
|
|
41
|
+
# fail-CLOSED on workdir failure (Axis-2 challenger MED): an unwritable TMPDIR must NOT masquerade as the
|
|
42
|
+
# design's valid "honest-empty queue" — a genuine infra failure and "no markers surfaced" are otherwise
|
|
43
|
+
# indistinguishable. Route infra failure to exit 2 (harness error), never to green-empty.
|
|
44
|
+
mkdir -p "$WORK" 2>/dev/null || { echo "❌ cannot create workdir under '${TMPDIR:-/tmp}' — cannot collect (NOT an empty queue)" >&2; exit 2; }
|
|
45
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
46
|
+
RAW="$WORK/raw"; : > "$RAW" 2>/dev/null || { echo "❌ cannot write workfile (NOT an empty queue)" >&2; exit 2; }
|
|
47
|
+
|
|
48
|
+
# --- LAYER 1: 6-source converge. Each source = a label + a glob; grep the convention line. ---
|
|
49
|
+
# (source label · file glob) — sources that don't exist yet simply contribute nothing (fail-visible below).
|
|
50
|
+
_pull() { # $1=source-label $2..=files
|
|
51
|
+
local label="$1"; shift
|
|
52
|
+
local f
|
|
53
|
+
for f in "$@"; do
|
|
54
|
+
[ -f "$f" ] || continue
|
|
55
|
+
# convention line: "CHAMBER-CANDIDATE: <desc>" (case-insensitive marker, desc after the colon)
|
|
56
|
+
grep -inE '^[[:space:]]*CHAMBER-CANDIDATE:' "$f" 2>/dev/null \
|
|
57
|
+
| sed -E 's/^[0-9]+:[[:space:]]*CHAMBER-CANDIDATE:[[:space:]]*//I' \
|
|
58
|
+
| while IFS= read -r desc; do
|
|
59
|
+
[ -n "$desc" ] && printf '%s\t%s\n' "$label" "$desc" >> "$RAW"
|
|
60
|
+
done
|
|
61
|
+
done
|
|
62
|
+
}
|
|
63
|
+
SRC_SEEN=0
|
|
64
|
+
for spec in \
|
|
65
|
+
"harness-doctor:$FH/tracks/_meta/*harness_doctor*.md" \
|
|
66
|
+
"harvest-loop:$FH/tracks/_audit/*.md" \
|
|
67
|
+
"fh-signal:$FH/tracks/_meta/fh_signal_*.md" \
|
|
68
|
+
"field-harvest:$FH/tracks/_contrib/*.md" \
|
|
69
|
+
"frontier-digest:$FH/tracks/_meta/frontier_digest_*.md" \
|
|
70
|
+
"uap:$FH/tracks/_meta/user_adaptation_profile.md" ; do
|
|
71
|
+
label="${spec%%:*}"; glob="${spec#*:}"
|
|
72
|
+
# word-split the glob deliberately (globbing); nullglob-safe via the -f check in _pull
|
|
73
|
+
files=$(ls $glob 2>/dev/null || true)
|
|
74
|
+
[ -n "$files" ] && SRC_SEEN=$((SRC_SEEN+1))
|
|
75
|
+
# shellcheck disable=SC2086
|
|
76
|
+
_pull "$label" $files
|
|
77
|
+
done
|
|
78
|
+
|
|
79
|
+
NRAW=$(grep -c . "$RAW" 2>/dev/null); NRAW=${NRAW:-0}
|
|
80
|
+
echo "── chamber candidate collect ──"
|
|
81
|
+
echo "sources present: $SRC_SEEN/6 · raw candidate markers found: $NRAW"
|
|
82
|
+
if [ "$NRAW" -eq 0 ]; then
|
|
83
|
+
echo "queue EMPTY — no 'CHAMBER-CANDIDATE:' markers in any source yet (convention not adopted / nothing surfaced)."
|
|
84
|
+
echo " This is an honest result, not an error: the discovery volume is n=0 until sources emit the marker."
|
|
85
|
+
echo " (Add 'CHAMBER-CANDIDATE: <desc>' to a harness-doctor / fh_signal / frontier-digest / harvest entry.)"
|
|
86
|
+
exit 0
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# --- keyword signature per candidate (for dedup) ---
|
|
90
|
+
STOP='^(that|this|from|into|over|when|what|will|your|there|their|then|than|with|chamber|candidate|skill|agent|tool|does|done|after|before|which|about|through)$'
|
|
91
|
+
_sig() { printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9가-힣' ' ' | tr -s ' ' '\n' \
|
|
92
|
+
| awk 'length($0)>=4' | grep -vE "$STOP" | sort -u; }
|
|
93
|
+
# kill-side signature for the seen-filter: keeps 2+ char tokens (a slug's meaningful short tokens like
|
|
94
|
+
# "qa"/"ui" are dropped by _sig's ≥4 filter → an all-short slug yields an EMPTY sig → kkn=0 → the old
|
|
95
|
+
# match never fired → a KILLed candidate re-entered the queue SILENTLY, Axis-2 MED-3b). Drops stopwords only.
|
|
96
|
+
_ksig() { printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9가-힣' ' ' | tr -s ' ' '\n' \
|
|
97
|
+
| awk 'length($0)>=2' | grep -vE "$STOP" | sort -u; }
|
|
98
|
+
|
|
99
|
+
# --- LAYER 2: dedup by greedy jaccard clustering ---
|
|
100
|
+
# cluster files: $WORK/cluster.N holds "source<TAB>desc" lines; $WORK/sig.N holds the union keyword sig.
|
|
101
|
+
NC=0
|
|
102
|
+
idx=0
|
|
103
|
+
while IFS=$'\t' read -r label desc; do
|
|
104
|
+
idx=$((idx+1))
|
|
105
|
+
_sig "$desc" > "$WORK/candsig.$idx"
|
|
106
|
+
merged=0
|
|
107
|
+
c=1
|
|
108
|
+
while [ "$c" -le "$NC" ]; do
|
|
109
|
+
inter=$(comm -12 "$WORK/sig.$c" "$WORK/candsig.$idx" 2>/dev/null | grep -c . || true)
|
|
110
|
+
union=$(sort -u "$WORK/sig.$c" "$WORK/candsig.$idx" 2>/dev/null | grep -c . || true)
|
|
111
|
+
jac=0; [ "${union:-0}" -gt 0 ] && jac=$(( inter * 100 / union ))
|
|
112
|
+
if [ "$jac" -ge "$MINJ" ]; then
|
|
113
|
+
printf '%s\t%s\n' "$label" "$desc" >> "$WORK/cluster.$c"
|
|
114
|
+
sort -u "$WORK/sig.$c" "$WORK/candsig.$idx" > "$WORK/sig.$c.tmp" && mv "$WORK/sig.$c.tmp" "$WORK/sig.$c"
|
|
115
|
+
merged=1; break
|
|
116
|
+
fi
|
|
117
|
+
c=$((c+1))
|
|
118
|
+
done
|
|
119
|
+
if [ "$merged" -eq 0 ]; then
|
|
120
|
+
NC=$((NC+1))
|
|
121
|
+
printf '%s\t%s\n' "$label" "$desc" > "$WORK/cluster.$NC"
|
|
122
|
+
cp "$WORK/candsig.$idx" "$WORK/sig.$NC"
|
|
123
|
+
fi
|
|
124
|
+
done < "$RAW"
|
|
125
|
+
|
|
126
|
+
# --- seen-filter: pull already-KILLed candidates from the G4 run ledger so a re-listed marker for a
|
|
127
|
+
# candidate the chamber already killed does NOT re-enter the main queue (the run-#3 real-use gap). It is
|
|
128
|
+
# EXCLUDED from the ranked queue but SURFACED in a trailing section (re-emit trigger visibility — a KILL
|
|
129
|
+
# is revisitable once its measured observation lands, so we don't silently erase it). Degrade: no ledger
|
|
130
|
+
# → no seen-filter (fail-visible; equals the pre-seen behavior, safe). ---
|
|
131
|
+
LEDGER="$FH/tracks/_chamber/INDEX.md"
|
|
132
|
+
KILLED="$WORK/killed"; : > "$KILLED"
|
|
133
|
+
if [ -f "$LEDGER" ]; then
|
|
134
|
+
# KILL rows: a run-log table data row (`| #N | date | candidate | VERDICT | ... |`) whose VERDICT
|
|
135
|
+
# field ($5) says KILL. Scope the KILL test to the verdict field — NOT a whole-line grep, which would
|
|
136
|
+
# match "kill" inside "skill" in any row's carry-text (Axis-2 HIGH-1: an EMIT row folding a sliver
|
|
137
|
+
# "into goal-quench skill" would be misread as KILL). Candidate = $4; strip backticks/asterisks/space.
|
|
138
|
+
awk -F'|' '$0 ~ /^\| *#/ && NF>=5 && toupper($5) ~ /KILL/ {print $4}' "$LEDGER" 2>/dev/null \
|
|
139
|
+
| sed 's/[`*]//g; s/^ *//; s/ *$//' | grep -v '^$' >> "$KILLED"
|
|
140
|
+
fi
|
|
141
|
+
NKILL=$(grep -c . "$KILLED" 2>/dev/null); NKILL=${NKILL:-0}
|
|
142
|
+
SEENOUT="$WORK/seen_out"; : > "$SEENOUT"
|
|
143
|
+
|
|
144
|
+
# --- LAYER 2: rank each cluster = source-diversity*2 + frequency; then screen for reinvention ---
|
|
145
|
+
RANKED="$WORK/ranked"; : > "$RANKED"
|
|
146
|
+
c=1
|
|
147
|
+
while [ "$c" -le "$NC" ]; do
|
|
148
|
+
freq=$(grep -c . "$WORK/cluster.$c" 2>/dev/null); freq=${freq:-0}
|
|
149
|
+
ndiv=$(cut -f1 "$WORK/cluster.$c" | sort -u | grep -c . || true)
|
|
150
|
+
srcs=$(cut -f1 "$WORK/cluster.$c" | sort -u | paste -sd, -)
|
|
151
|
+
rep=$(head -1 "$WORK/cluster.$c" | cut -f2-)
|
|
152
|
+
score=$(( ndiv * 2 + freq ))
|
|
153
|
+
# seen-filter: does this candidate match an already-KILLed ledger entry? Two decorrelated tests, either
|
|
154
|
+
# sufficient: (1) kill-name keyword recall ≥60% (the common multi-token case); (2) normalized-substring
|
|
155
|
+
# fallback — the flattened slug (alnum-only, ≥5 chars) appearing in the flattened candidate — which
|
|
156
|
+
# closes the all-short-token silent-re-entry hole (MED-3b) that recall alone leaves open. Residual
|
|
157
|
+
# (documented, accepted): a paraphrased re-listing (router→routing) can still under-recall (MED-4), and
|
|
158
|
+
# a generic slug can over-exclude — but over-exclusion is VISIBLE in the SEEN-KILLED section (operator
|
|
159
|
+
# catches a wrong match), whereas silent re-entry was invisible. Visible-imprecise ≻ silent-miss.
|
|
160
|
+
if [ "$NKILL" -gt 0 ]; then
|
|
161
|
+
_ksig "$rep" > "$WORK/repsig"
|
|
162
|
+
repflat=$(printf '%s' "$rep" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9')
|
|
163
|
+
seen_match=""
|
|
164
|
+
while IFS= read -r kname; do
|
|
165
|
+
[ -z "$kname" ] && continue
|
|
166
|
+
_ksig "$kname" > "$WORK/ksig"
|
|
167
|
+
kkn=$(grep -c . "$WORK/ksig" 2>/dev/null || true); kkn=${kkn:-0}
|
|
168
|
+
matched=0; [ "$kkn" -gt 0 ] && matched=$(comm -12 "$WORK/ksig" "$WORK/repsig" 2>/dev/null | grep -c . || true)
|
|
169
|
+
kpct=0; [ "$kkn" -gt 0 ] && kpct=$(( matched * 100 / kkn ))
|
|
170
|
+
kflat=$(printf '%s' "$kname" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9')
|
|
171
|
+
if [ "$kkn" -gt 0 ] && [ "$kpct" -ge 60 ]; then seen_match="$kname"; break; fi
|
|
172
|
+
if [ "${#kflat}" -ge 5 ] && printf '%s' "$repflat" | grep -qF "$kflat"; then seen_match="$kname"; break; fi
|
|
173
|
+
done < "$KILLED"
|
|
174
|
+
if [ -n "$seen_match" ]; then
|
|
175
|
+
printf '%s\t%s\n' "$seen_match" "$rep" >> "$SEENOUT"
|
|
176
|
+
c=$((c+1)); continue
|
|
177
|
+
fi
|
|
178
|
+
fi
|
|
179
|
+
# reinvention first-pass on the representative description. Capture BOTH the verdict AND the matched
|
|
180
|
+
# anchor the screener already emits ("top asset overlap: X") so a DUPLICATE-CANDIDATE is ACTIONABLE
|
|
181
|
+
# ("DUP:skill:goal-quench") instead of a uniform non-discriminating flag (Axis-2 challenger Axis-5).
|
|
182
|
+
verdict="?"
|
|
183
|
+
if [ -x "$SCREEN" ] || [ -f "$SCREEN" ]; then
|
|
184
|
+
sout=$(bash "$SCREEN" "$rep" 2>/dev/null)
|
|
185
|
+
verdict=$(printf '%s\n' "$sout" | grep -oE 'VERDICT: [A-Z-]+' | head -1 | sed 's/VERDICT: //')
|
|
186
|
+
[ -z "$verdict" ] && verdict="?"
|
|
187
|
+
if [ "$verdict" = "DUPLICATE-CANDIDATE" ]; then
|
|
188
|
+
anchor=$(printf '%s\n' "$sout" | grep -oE 'top asset overlap: [^ ]+' | head -1 | sed 's/top asset overlap: //')
|
|
189
|
+
[ -n "$anchor" ] && verdict="DUP:$anchor"
|
|
190
|
+
fi
|
|
191
|
+
fi
|
|
192
|
+
printf '%03d\t%s\t%s\t%s\t%s\n' "$score" "$ndiv" "$srcs" "$verdict" "$rep" >> "$RANKED"
|
|
193
|
+
c=$((c+1))
|
|
194
|
+
done
|
|
195
|
+
|
|
196
|
+
echo "deduped candidates: $NC (from $NRAW raw markers)"
|
|
197
|
+
echo ""
|
|
198
|
+
printf '%-5s %-6s %-24s %-18s %s\n' "SCORE" "SRCS" "SOURCES" "REINVENTION" "CANDIDATE"
|
|
199
|
+
printf '%-5s %-6s %-24s %-18s %s\n' "-----" "------" "------------------------" "------------------" "---------"
|
|
200
|
+
sort -rn "$RANKED" | while IFS=$'\t' read -r score ndiv srcs verdict rep; do
|
|
201
|
+
# drop leading zeros for display
|
|
202
|
+
printf '%-5d %-6d %-24s %-18s %s\n' "$((10#$score))" "$ndiv" "$srcs" "$verdict" "$rep"
|
|
203
|
+
done
|
|
204
|
+
echo ""
|
|
205
|
+
echo "NOTE: SCORE = source-diversity×2 + frequency. REINVENTION is a first-pass flag (DUPLICATE-CANDIDATE"
|
|
206
|
+
echo "= HITL KILL review, not auto-drop). Uncertainty/failure-cost stay JUDGED — operator picks which"
|
|
207
|
+
echo "survivors actually enter the chamber (goal-quench budget gate + HITL). This tool ranks; it never admits."
|
|
208
|
+
|
|
209
|
+
# seen-filter trailing section: candidates the G4 ledger already KILLed are shown here (NOT in the ranked
|
|
210
|
+
# queue) so a re-listed marker does not silently re-enter the chamber, yet the KILL stays visible (a KILL
|
|
211
|
+
# is revisitable once its re-emit trigger lands — see the run's EMISSION_VERDICT for that condition).
|
|
212
|
+
if [ -s "$SEENOUT" ]; then
|
|
213
|
+
echo ""
|
|
214
|
+
echo "SEEN-KILLED — excluded from the queue (G4 ledger $LEDGER already KILLed these):"
|
|
215
|
+
while IFS=$'\t' read -r kn rep; do
|
|
216
|
+
[ -z "$rep" ] && continue
|
|
217
|
+
echo " · \"$rep\" ↔ killed as \"$kn\" (재등재 스킵; re-emit only when that run's trigger condition is met)"
|
|
218
|
+
done < "$SEENOUT"
|
|
219
|
+
elif [ ! -f "$LEDGER" ]; then
|
|
220
|
+
echo ""
|
|
221
|
+
echo "SEEN-FILTER: skipped — no G4 ledger at $LEDGER (fail-visible; no seen-exclusion applied this run)."
|
|
222
|
+
fi
|
|
223
|
+
exit 0
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# chamber_run.sh — chamber run orchestrator (the runner the skeleton's gaps G1-forcing/G2/G4/STATUS all
|
|
3
|
+
# hung on). GLUE ONLY: it wires the pieces that already exist (workspace convention · budget gate notion ·
|
|
4
|
+
# the isolated persona agents · the Emission Gate · the G4 ledger) into one intent-driven, resumable flow
|
|
5
|
+
# so a chamber run can be *completed by intent* rather than hand-followed from the skeleton doc.
|
|
6
|
+
#
|
|
7
|
+
# What it MECHANIZES: workspace + INTENT/BUDGET templates · STATUS stamping (resumable — re-run to advance) ·
|
|
8
|
+
# the budget-entry gate (G2: blocks step 4 until an ESTIMATE is recorded — no uncapped run) · the step-4
|
|
9
|
+
# isolation gate (G1-forcing: blocks step 5 until SIM_NOTES has ≥3 blind persona sections) · the Emission
|
|
10
|
+
# Gate verdict capture · actual-cost record · and the G4 ledger auto-append (idempotent).
|
|
11
|
+
#
|
|
12
|
+
# What it CANNOT mechanize (honest muscle boundary, documented in CHAMBER_RUN_SKELETON.md): bash cannot
|
|
13
|
+
# spawn the isolated Agents itself. Step 4 PRINTS the exact dispatch and GATES on the ≥3 persona artifact —
|
|
14
|
+
# the human/Claude does the actual `fh-meta:{beginner,challenger,main-player}` dispatch. Isolation stays a
|
|
15
|
+
# salience+artifact gate, not a spawn. Budget/cost numbers calibrate only across real runs (muscle, not wiring).
|
|
16
|
+
#
|
|
17
|
+
# Usage: bash scripts/chamber_run.sh <candidate-slug> # create/advance the run (idempotent)
|
|
18
|
+
# bash scripts/chamber_run.sh <candidate-slug> status # show where the run is
|
|
19
|
+
# exit 0 = advanced or already complete · exit 1 = blocked on a missing artifact (message says which)
|
|
20
|
+
# exit 2 = harness error (FH root / bad slug)
|
|
21
|
+
|
|
22
|
+
set -uo pipefail
|
|
23
|
+
|
|
24
|
+
FH="$(cd "$(dirname "$0")/.." && pwd)"
|
|
25
|
+
if [ ! -d "$FH/tracks" ] || [ ! -d "$FH/plugins" ]; then
|
|
26
|
+
echo "❌ FH root not found at '$FH' — run from the FH repo." >&2; exit 2
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
SLUG="${1:-}"
|
|
30
|
+
[ -z "$SLUG" ] && { echo "usage: chamber_run.sh <candidate-slug> [status]" >&2; exit 2; }
|
|
31
|
+
# slug charset: [A-Za-z0-9-] only, no leading dash. Rejecting regex metachars (`.` `+` `[` `*`) is
|
|
32
|
+
# load-bearing — $SLUG is interpolated raw into an ERE idempotency grep below; `a.b` would let `.` match
|
|
33
|
+
# any char (idempotency mismatch → duplicate ledger row), `a+b` would break the ERE (Axis-2 LOW-5).
|
|
34
|
+
case "$SLUG" in -*) echo "❌ bad slug '$SLUG' (no leading dash)" >&2; exit 2 ;; esac
|
|
35
|
+
case "$SLUG" in *[!A-Za-z0-9-]*) echo "❌ bad slug '$SLUG' (allowed: letters, digits, hyphen)" >&2; exit 2 ;; esac
|
|
36
|
+
CMD="${2:-advance}"
|
|
37
|
+
|
|
38
|
+
WS="$FH/tracks/_chamber/$SLUG"
|
|
39
|
+
LEDGER="$FH/tracks/_chamber/INDEX.md"
|
|
40
|
+
STATUS_F="$WS/STATUS"
|
|
41
|
+
TODAY="$(date +%Y-%m-%d)"
|
|
42
|
+
|
|
43
|
+
_status() { [ -f "$STATUS_F" ] && cat "$STATUS_F" || echo "step-0"; }
|
|
44
|
+
_stamp() { printf '%s\n' "$1" > "$STATUS_F"; }
|
|
45
|
+
|
|
46
|
+
if [ "$CMD" = "status" ]; then
|
|
47
|
+
echo "chamber run '$SLUG' → STATUS: $(_status)"
|
|
48
|
+
[ -d "$WS" ] && ls -1 "$WS" 2>/dev/null | sed 's/^/ /'
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
echo "── chamber run: $SLUG (STATUS: $(_status)) ──"
|
|
53
|
+
|
|
54
|
+
# STEP 1 — workspace
|
|
55
|
+
if [ ! -d "$WS" ]; then
|
|
56
|
+
mkdir -p "$WS" 2>/dev/null || { echo "❌ cannot create workspace $WS" >&2; exit 2; }
|
|
57
|
+
echo " ✓ step 1: workspace created ($WS)"
|
|
58
|
+
fi
|
|
59
|
+
_stamp "step-1-done"
|
|
60
|
+
|
|
61
|
+
# STEP 2 — INTENT.md (template if absent; block until it has real content)
|
|
62
|
+
if [ ! -f "$WS/INTENT.md" ]; then
|
|
63
|
+
cat > "$WS/INTENT.md" <<EOF
|
|
64
|
+
# INTENT — $SLUG (chamber run)
|
|
65
|
+
|
|
66
|
+
## Candidate intent
|
|
67
|
+
<one line: the capability/project to incubate>
|
|
68
|
+
|
|
69
|
+
## Success conditions (each with a check class: mandatory-pass / measured / judged)
|
|
70
|
+
1.
|
|
71
|
+
2.
|
|
72
|
+
|
|
73
|
+
## Failure cost (blast radius AND reinvention risk)
|
|
74
|
+
-
|
|
75
|
+
|
|
76
|
+
## Chamber metadata
|
|
77
|
+
- entry reason: <uncertain | exploratory | failure-expensive | high-reinvention-risk>
|
|
78
|
+
- date: $TODAY
|
|
79
|
+
EOF
|
|
80
|
+
echo " ⛔ step 2 BLOCKED: fill in $WS/INTENT.md (template written), then re-run."; exit 1
|
|
81
|
+
fi
|
|
82
|
+
if grep -q '<one line: the capability' "$WS/INTENT.md"; then
|
|
83
|
+
echo " ⛔ step 2 BLOCKED: $WS/INTENT.md still has the placeholder — fill it, then re-run."; exit 1
|
|
84
|
+
fi
|
|
85
|
+
# gate (a) is "artifact exists WITH real content", not just "placeholder removed" (Axis-2 LOW-6): require
|
|
86
|
+
# at least one non-empty numbered success condition so a gutted INTENT.md doesn't pass.
|
|
87
|
+
if ! awk '/^## Success conditions/{f=1;next} /^## /{f=0} f && /^[0-9]+\.[[:space:]]*[^[:space:]]/{print; exit}' "$WS/INTENT.md" | grep -q .; then
|
|
88
|
+
echo " ⛔ step 2 BLOCKED: $WS/INTENT.md has no filled success condition (need a numbered line with content), re-run."; exit 1
|
|
89
|
+
fi
|
|
90
|
+
_stamp "step-2-done"; echo " ✓ step 2: INTENT.md present"
|
|
91
|
+
|
|
92
|
+
# STEP 3 — budget-entry gate (G2). Cannot invoke goal-quench from bash; MECHANICALLY require a recorded
|
|
93
|
+
# estimate before any (expensive) simulation runs. No ESTIMATE = no run — that IS the entry cap.
|
|
94
|
+
if [ ! -f "$WS/BUDGET.md" ]; then
|
|
95
|
+
cat > "$WS/BUDGET.md" <<EOF
|
|
96
|
+
# BUDGET — $SLUG (chamber run)
|
|
97
|
+
|
|
98
|
+
# Route through goal-quench's budget gate for an expensive run, then record here.
|
|
99
|
+
# Demo-scale runs may self-cap — but an ESTIMATE line is mandatory (this is the entry cap).
|
|
100
|
+
ESTIMATE: <e.g. ~3 persona dispatches, demo-scale, self-capped | or a token budget>
|
|
101
|
+
ACTUAL: <filled at step 6>
|
|
102
|
+
EOF
|
|
103
|
+
echo " ⛔ step 3 BLOCKED: record an ESTIMATE in $WS/BUDGET.md (budget-entry gate G2), then re-run."; exit 1
|
|
104
|
+
fi
|
|
105
|
+
if grep -qE '^ESTIMATE:[[:space:]]*<' "$WS/BUDGET.md" || ! grep -qE '^ESTIMATE:[[:space:]]*\S' "$WS/BUDGET.md"; then
|
|
106
|
+
echo " ⛔ step 3 BLOCKED: $WS/BUDGET.md ESTIMATE is empty/placeholder (G2 entry cap), then re-run."; exit 1
|
|
107
|
+
fi
|
|
108
|
+
_stamp "step-3-done"; echo " ✓ step 3: budget ESTIMATE recorded (entry cap satisfied)"
|
|
109
|
+
|
|
110
|
+
# STEP 4 — persona simulation (G1-forcing gate). Dispatch is human/Claude-side (bash can't spawn Agents);
|
|
111
|
+
# gate on the ≥3 blind persona artifact. Isolation is the mechanism — the runner enforces the artifact, not the spawn.
|
|
112
|
+
if [ ! -f "$WS/SIM_NOTES.md" ]; then
|
|
113
|
+
echo " ⛔ step 4 BLOCKED: dispatch 3 BLIND ISOLATED Agents and record each in $WS/SIM_NOTES.md:"
|
|
114
|
+
echo " Agent fh-meta:beginner → first-contact friction"
|
|
115
|
+
echo " Agent fh-meta:main-player → daily-use / target-user value"
|
|
116
|
+
echo " Agent fh-meta:challenger → skeptic: emit value? failure cost? what's invisible?"
|
|
117
|
+
echo " Each as '## <persona> ...' section. (sim-conductor fills persona_container_schema's 6 slots.)"
|
|
118
|
+
exit 1
|
|
119
|
+
fi
|
|
120
|
+
# count DISTINCT personas (not raw lines — 3×"## beginner" must NOT satisfy the 3-blind-persona gate).
|
|
121
|
+
NPERS=0
|
|
122
|
+
for _p in beginner main-player challenger; do
|
|
123
|
+
grep -iqE "^##[[:space:]].*$_p" "$WS/SIM_NOTES.md" 2>/dev/null && NPERS=$((NPERS+1))
|
|
124
|
+
done
|
|
125
|
+
if [ "$NPERS" -lt 3 ]; then
|
|
126
|
+
echo " ⛔ step 4 BLOCKED: SIM_NOTES.md has $NPERS/3 DISTINCT blind persona sections (need all of beginner + main-player + challenger)."; exit 1
|
|
127
|
+
fi
|
|
128
|
+
_stamp "step-4-done"; echo " ✓ step 4: $NPERS blind persona sections present (isolation-gate satisfied)"
|
|
129
|
+
|
|
130
|
+
# STEP 5 — Emission Gate. Require a VERDICT: EMIT | PARTIAL-EMIT | KILL.
|
|
131
|
+
if [ ! -f "$WS/EMISSION_VERDICT.md" ]; then
|
|
132
|
+
cat > "$WS/EMISSION_VERDICT.md" <<EOF
|
|
133
|
+
# Emission Gate Verdict — $SLUG (chamber run)
|
|
134
|
+
|
|
135
|
+
VERDICT: <EMIT | PARTIAL-EMIT | KILL>
|
|
136
|
+
|
|
137
|
+
## Judged: does the simulation hold? (+ mechanical anchor: overlap grep / gate verdicts / reproduced flows)
|
|
138
|
+
|
|
139
|
+
## Carry-forward (what compounds into the next run)
|
|
140
|
+
-
|
|
141
|
+
EOF
|
|
142
|
+
echo " ⛔ step 5 BLOCKED: decide WITH the operator (HITL), record VERDICT in $WS/EMISSION_VERDICT.md, re-run."; exit 1
|
|
143
|
+
fi
|
|
144
|
+
# PARTIAL-EMIT listed FIRST in every alternation so it is never mis-extracted as its EMIT substring.
|
|
145
|
+
VERDICT=$(grep -ioE '^VERDICT:[[:space:]]*(PARTIAL-EMIT|EMIT|KILL)' "$WS/EMISSION_VERDICT.md" 2>/dev/null | head -1 | grep -ioE 'PARTIAL-EMIT|EMIT|KILL' | head -1 | tr 'a-z' 'A-Z')
|
|
146
|
+
# a bare "## Verdict:" prose line (run #3 style) also counts if it names KILL/EMIT
|
|
147
|
+
[ -z "$VERDICT" ] && VERDICT=$(grep -ioE 'VERDICT[: *]+\**(PARTIAL-EMIT|EMIT|KILL)' "$WS/EMISSION_VERDICT.md" 2>/dev/null | grep -ioE 'PARTIAL-EMIT|EMIT|KILL' | head -1 | tr 'a-z' 'A-Z')
|
|
148
|
+
if [ -z "$VERDICT" ]; then
|
|
149
|
+
echo " ⛔ step 5 BLOCKED: no VERDICT (EMIT|PARTIAL-EMIT|KILL) found in $WS/EMISSION_VERDICT.md, re-run."; exit 1
|
|
150
|
+
fi
|
|
151
|
+
_stamp "step-5-done"; echo " ✓ step 5: Emission Gate verdict = $VERDICT"
|
|
152
|
+
|
|
153
|
+
# STEP 6 — actual cost / carry-forward record.
|
|
154
|
+
if grep -qE '^ACTUAL:[[:space:]]*<' "$WS/BUDGET.md" || ! grep -qE '^ACTUAL:[[:space:]]*\S' "$WS/BUDGET.md"; then
|
|
155
|
+
echo " ⛔ step 6 BLOCKED: record ACTUAL cost in $WS/BUDGET.md (actual-vs-estimate calibration), re-run."; exit 1
|
|
156
|
+
fi
|
|
157
|
+
_stamp "step-6-done"; echo " ✓ step 6: actual cost recorded"
|
|
158
|
+
|
|
159
|
+
# STEP 7 — terminus + G4 ledger auto-append (idempotent).
|
|
160
|
+
if [ ! -f "$LEDGER" ]; then
|
|
161
|
+
echo " ⚠ step 7: no ledger at $LEDGER — skipping auto-append (fail-visible)."
|
|
162
|
+
elif grep -qE "^\|[^|]*\|[^|]*\|[^|]*\`$SLUG\`" "$LEDGER"; then
|
|
163
|
+
echo " ✓ step 7: ledger already has a row for '$SLUG' (idempotent — no duplicate append)."
|
|
164
|
+
else
|
|
165
|
+
NEXT=$(grep -oE '^\|[[:space:]]*#([0-9]+)' "$LEDGER" | grep -oE '[0-9]+' | sort -n | tail -1)
|
|
166
|
+
NEXT=$(( ${NEXT:-0} + 1 ))
|
|
167
|
+
CARRY=$(grep -A2 -iE '^##[[:space:]]*Carry-forward' "$WS/EMISSION_VERDICT.md" 2>/dev/null | grep -E '^-[[:space:]]*\S' | head -1 | sed 's/^-[[:space:]]*//; s/|/·/g')
|
|
168
|
+
[ -z "$CARRY" ] && CARRY="see $SLUG/EMISSION_VERDICT.md"
|
|
169
|
+
printf '| #%s | %s | `%s` | **%s** | %s | `%s/` |\n' "$NEXT" "$TODAY" "$SLUG" "$VERDICT" "$CARRY" "$SLUG" >> "$LEDGER"
|
|
170
|
+
echo " ✓ step 7: appended run #$NEXT ($VERDICT) to the G4 ledger."
|
|
171
|
+
fi
|
|
172
|
+
_stamp "step-7-done"
|
|
173
|
+
|
|
174
|
+
echo ""
|
|
175
|
+
case "$VERDICT" in
|
|
176
|
+
EMIT) echo "TERMINUS (EMIT): route by class — field harness → Full-Harness Mode (auto_project_mapping.md §6);"
|
|
177
|
+
echo " FH-internal utility → New-Skill Pre-Commit gate + asset-placement-gate." ;;
|
|
178
|
+
PARTIAL-EMIT) echo "TERMINUS (PARTIAL-EMIT): the standing candidate is killed; fold the surviving sliver into an"
|
|
179
|
+
echo " existing asset / the skeleton (no new asset). Workspace stays as evidence." ;;
|
|
180
|
+
KILL) echo "TERMINUS (KILL): first-class success — a cheap run prevented a speculative/reinvention build."
|
|
181
|
+
echo " No emit. Workspace stays as the evidence record; seen-filter will skip re-listing it." ;;
|
|
182
|
+
esac
|
|
183
|
+
echo "chamber run '$SLUG' COMPLETE (STATUS: step-7-done, verdict $VERDICT)."
|
|
184
|
+
exit 0
|