| name | ocas-forge | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| description | Skill architect and builder. Designs, builds, and validates complete Agent Skill packages through a mandatory eight-phase pipeline: existence gate, research, classify, scope, architecture, plan, build, validate. Default output is the finished installable package. Not for skill evaluation (use skilllab's Critique procedure) or variant proposals (use ocas-mentor). | ||||||||||||||
| license | MIT | ||||||||||||||
| source | https://github.com/indigokarasu/forge | ||||||||||||||
| includes |
|
||||||||||||||
| metadata |
|
||||||||||||||
| tags |
|
||||||||||||||
| triggers |
|
Forge is the system's skill architect — given a capability idea or broken existing package, it runs a mandatory eight-phase internal pipeline covering existence gate, research, classification, scoping, architecture, planning, construction, and validation before writing a single file. The default output is the finished, installable package with all file contents written; Forge never returns design briefs or plans in place of the real artifact.
- Building new OCAS skills from scratch
- Skill architecture and design review
- Bulk skill library updates and synchronization
- Skill consolidation and deprecation
- When a new capability needs a permanent skill home
- Create a new Agent Skill from a goal or capability description
- Review or critique an existing skill package
- Repair broken or defective skill packages
- Validate a skill package against OCAS standards
- One-off task execution (use the appropriate existing skill)
- Skill evaluation/scoring (use Mentor or skilllab)
- Content generation or research
- System health monitoring (use Custodian)
- Authentication and service wiring (use ocas-auth)
- Building non-skill artifacts
- Web research — use Sift
- Skill structure reference / frontmatter template lookup — use
write-a-skillfor quick reference on field requirements, file layout, and description conventions. Forge is for building.
Forge owns skill design, construction, consolidation, update verification,
compliance auditing, and repo-sync. Forge's forge.validate handles quick
structural checks; deep quality scoring and iterative improvement is now
owned by skilllab (Critique procedure, merged from ocas-critique).
Forge does not own: skill quality scoring and iteration (skilllab), skill evaluation or variant testing (Mentor), behavioral pattern analysis, behavioral refinement (Praxis), experimentation (Fellow), system health and skill initialization (Custodian), runtime orchestration and delegation (the agent harness), authentication and MCP wiring (ocas-auth).
Note on review-skill vs skilllab's Critique: The 3rd-party review-skill
(agentskill-sh) provides lightweight quick-check scoring. skilllab's Critique
procedure is the full OCAS quality engine with 6-phase pipeline, batch mode,
iteration loops, and autonomous improvement. Use skilllab for all OCAS skill
scoring. Use review-skill only for quick structural checks on non-OCAS
skills. Never
push review-skill to GitHub — it's 3rd-party.
Forge receives VariantProposal and VariantDecision files from Mentor. It builds variant packages and applies promotion decisions.
- Concept/Event — projects, tasks, skill performance evaluations, OKR cycles
- Concept/Idea — improvement proposals, behavioral patterns
- Thing/DigitalArtifact — project state records, task graphs, evaluation reports
Mentor does not emit entity signals directly. Journal outputs are ingested by Chronicle for knowledge persistence.
forge.build— design, scope, build, and validate a complete skill packageforge.critique— review a package and identify defectsforge.repair— fix broken files in an existing packageforge.classify— classify a proposed skill (shortcut, workflow, system)forge.validate— run validation checks on a packageforge.scaffold— generate a minimal package skeletonforge.consolidate— merge an orphan or duplicate skill into its natural parentforge.verify-update— check whether a skill is at the latest version from its GitHub sourceforge.sync— sync local skill changes to the canonical repository via PRforge.audit— audit one or more skills for OCAS compliance, apply fixes, and sync to GitHubforge.status— current build state if multi-step build in progressforge.journal— write journal for the current run; called at end of every runforge.update— pull latest from GitHub source; preserves journals and data
Run all phases before writing files. Full phase detail including existence gates
(parent search, standalone test, absorption test), research (skill library → GitHub search → deep-read → compare),
classification, scoping, architecture, plan, build, and validation procedures: see
references/design_pipeline.md.
Key rule: absorption first. If an existing skill already owns the domain,
add content to it as a references/ doc or scripts/ file — do not create a
new skill. See references/enforcement_durability.md for the absorption
decision framework.
Research rule: After the existence gate passes, you MUST research before
classifying. Search GitHub repos (via gh search repos) AND search the skill
library (via the APIs below) to understand what already exists. The goal is
NOT to copy existing skills but to understand how they work and synthesize
that knowledge into a new, better skill. Review at least 10 repos or skills
before deciding to build.
See references/naming-and-authorship.md for the full naming convention and authorship tagging rules. Key points:
- Never create
ocas-*or rename toocas-*/util-*without explicit user authorization. - Auto-generated skills must use
author: autogeneratedin metadata. - Auto-generated thin wrappers are candidates for deletion — see references for criteria.
- Shortcut — narrow tool wrapper. 20-120 line SKILL.md.
- Workflow — multi-step process. 80-250 line SKILL.md.
- System — durable behavior system. 150-300 line SKILL.md, deeper material in references.
Minimum package: SKILL.md with agentskills.io frontmatter. Add references/, scripts/, assets/ only when justified. Read references/enforcement_durability.md for full guidance on how to make rules durable across updates. See also references/package_patterns.md for package shape guidance and references/authoring_rules.md for full authoring standards.
After every Forge command (build, critique, repair, validate, audit):
- Check
{agent_root}/commons/data/ocas-forge/for unprocessed VariantProposal and VariantDecision JSON files. Cross-reference proposal IDs againstintake/processed/andprocessed/directories to skip already-processed files. Process any new files — build variant packages, apply fixes, or queue for Mentor evaluation as appropriate. After processing, move files toprocessed/. - Check journal payload fields (see interfaces specification) for VariantProposal and VariantDecision files from Mentor received via journal; process and move to the consumer's ingestion log.
- Persist build log entries and decisions to local JSONL files.
- Log material decisions to
decisions.jsonl. - Write journal via
forge.journal.
When to apply fixes directly vs. build variants: If a proposal has strong evidence (≥3 consecutive proposals for the same issue, ≥50 runs analyzed, ≥7 days of consistent data, and the fix is low-risk), Forge may apply the fix directly without a full A/B evaluation cycle. Otherwise, queue for Fellow evaluation. Document the rationale in the action journal.
Skills that hardcode ~/.hermes/ paths will NOT work on other agent harnesses (OpenClaw, Claude Code, Cursor, etc.). When building a new skill:
- Use
{agent_root}as the base for all paths inside the skill's storage layout diagrams. This variable resolves to whatever harness the skill runs on. - NEVER hardcode
~/.hermes/in file paths, storage diagrams, or operational descriptions. Even for Hermes-native skills, use{agent_root}/sessions/,{agent_root}/skills/,{agent_root}/references/instead. - Mention the target harness in the frontmatter with a
requires:field if the skill depends on Hermes-specific tools (memory,skill_manage,session_search,cronjob). Example:requires: hermes. This tells other harnesses to skip the skill. - Document Hermes-specific tool dependencies in a "Required tools" section so future porters know what to adapt.
- Skipping research on skill improvement. Phase 1.5 (Research) is mandatory for ALL forge operations — not just new builds. When asked to "improve" or "update" an existing skill, you MUST still research external sources (GitHub, arxiv, community patterns) to find new patterns, techniques, and taxonomies that could improve the skill. The user correction "did you do the research phase?" is a signal that you skipped Phase 1.5. Research is not optional just because the skill already exists — the whole point of improvement is to find what you don't already know.
- Vague or overly broad scope
- Generic descriptions that don't route well
- SKILL.md bloated with background explanation
- Support folders created for aesthetics
- Plans returned instead of packages
- Template residue and placeholders
- Storage inside skill package directories
- Undocumented inter-skill interfaces
## Integrated:wrapper sections: when folding content into a parent skill, do NOT wrap it in## Integrated:sections. Refactor the content into the parent's existing section structure instead.- Advisory-only enforcement doesn't work: writing "use Forge instead of skill_manage" in MEMORY.md is advisory and easily skipped. The hard gates must be in the Forge SKILL.md itself (phase 1 checks A/B/C), because that's the artifact that gets loaded and followed. Never rely on memory notes as the sole enforcement mechanism for behavioral rules.
- Fixed argparse nargs error in run_dispatch_pipeline.py — Changed
nargs='[]'tonargs='*'for the --new-files argument to accept zero or more arguments. This fixed a ValueError that occurred when the script was called during dispatch processing. - Stale files in
processed/vsintake/processed/— check both locations during journal-scan proposals/AND top-levelprocessed/are SOURCE MIRRORS, not pending work (confirmed 2026-07-13, RE-CONFIRMED 2026-07-16): When scanning for unprocessed variant proposals (vp_*.json/vd_*.json), count ONLY files incommons/data/ocas-forge/intake/that are NOT already inintake/processed/. Do NOT count files undercommons/data/ocas-forge/proposals/(a source mirror already copied intointake/processed/) NOR the top-levelcommons/data/ocas-forge/processed/dir (another mirror). A naive recursivefind/os.walkover the WHOLEocas-forgetree sweeps up BOTH mirrors and overcounts, falsely flipping aroutine_no_opdispatch into agenuinevariant-build dispatch — this bit a 2026-07-16 closure orchestrator that recursive-walked the whole tree, found 11 copies inproposals/+ 11 in top-levelprocessed/, and wroteunprocessed_proposals: 11/action: genuinewhen the true value was 0. USEscripts/forge_count_unprocessed.py(bounded walk ofintake/only) instead of any hand-rolled recursive count. After counting, cross-check against the priorforge-scan-*.jsonjournal'sunprocessed_proposalsfield; if the prior scan said 0 and no new variant work arrived, the real count is 0. If you already wrote a false-positive journal, patch it tounprocessed_proposals: 0/action: routine_no_opBEFORE bridging.- Missing
includes:in frontmatter — required when references/ or scripts/ dir exists - Scope boundary for sync —
forge.sync/forge.auditonly onocas-*skills - Doing more than asked — match work to the scope of the request
- Incorrect Naming — NEVER create/rename
ocas-*without user authorization - Non-durable fixes — put rules in skill's own git repo or MEMORY.md, not hermes core
- Runaway repo creation — check for 3rd-party skills before
gh repo create - YAML block scalar truncation —
description: >/|contain newlines; useread_file+patch actionfield is polymorphic in forge journals — guard withisinstancebefore every access- Dispatcher
new_filespaths lack prefix — AND the dispatch-wave journal resolves to the WRONG tree (confirmed 2026-07-16) — The dispatcher'sdetails.new_filesprintsocas-dispatch/2026-07-16/dispatch-wave-20260716T195230Z.json. Treated as profile-relative, that resolves tocommons/data/ocas-dispatch/<DATE>/— but that directory holds only LEGACY mirror dispatch-wave JSONs (2026-06 era). The LIVE on-disk journal is atcommons/journals/ocas-dispatch/2026-07-16/dispatch-wave-20260716T195230Z.json. Aread_fileon the dispatcher-printed path returns "File not found." Route all dispatch-wave journal reads to thecommons/journals/tree (the same treeverify_genuine_gap_profile.py/closure_convergence_sweep.pywalk). Usefind /root/.hermes -name 'dispatch-wave-*.json'to locate the real path. verify_eval_no_phantoms.pyreports PRE-EXISTING historical phantoms — NEVER run destructive--fixduring a closure pass (confirmed 2026-07-16) — The script walks the ENTIRE eval store (not date-scoped) and reports 1,800+ phantoms: historical 2026-06 → 2026-07-14 entries, malformed arg-flag leaks (--help,--apply,--path), and mis-namespaced relpaths (e.g.ocas-dispatch/dispatch-wave-20260716T012901Z.jsonmissing its date dir). NONE belong to the current wave. Running--fixwould mass-delete historical eval lines — OUT OF SCOPE and RISKY during a dispatch closure. Correct scope check for a closure: (a) verify every entry THIS run bridged wasos.path.exists()-confirmed BEFORE bridging, and (b) assertGENUINE GAP = 0viaverify_genuine_gap_profile.py --date <DATE>. Leave historical phantom cleanup to a dedicated audit, not a closure pass.- Appending to JSONL files — ALWAYS use
echo >>, NEVER heredoc with> write_fileescapes quotes in Python files — useterminal()with heredoc for .py files- Programmatic SKILL.md surgery can delete the frontmatter
---fence (confirmed 2026-07-16 grind): When you use a Python slice to delete/replace a block ofSKILL.md— e.g. extracting inline operational detail intoreferences/to cut the line count — a straydel lines[a:b]or aheader + pointer + restreassembly that accidentally includes the frontmatter's closing---will silently remove the YAML delimiter. The next parse treats the body as frontmatter (or raisesyaml.ScannerError), and the 10khr scorer drops D1 → 1 (name/description unparseable) and D2 → 3. This is invisible until you re-score. Fix: (1) After ANY programmatic SKILL.md edit, immediately re-runscripts/critique_10khr_runner.py --report-only(or callscore_skill()) and watch D1/D2 — a sudden drop from 5 to ≤3 means the fence is gone. (2) Guard the slice to stop before the---line; if eaten, re-insert---at the frontmatter/body boundary. (3) Verify before declaring done:yaml.safe_load(content.split('---')[1])must succeed and returnname/description. The same guard applies toforge.build/forge.repairedits done viaterminal()heredoc slices. - Eval-store manual grep must use the BARE relpath — NOT
commons/journals/-prefixed — When you manuallygrepthe praxis eval store (commons/data/ocas-praxis/journals_evaluated.jsonl, keyjournal_id) to check whether a journal was already evaluated, grep with the bare relpathocas-mentor/2026-07-16/mentor-light-XXXX.json, NOT the prefix-inclusivecommons/journals/ocas-mentor/2026-07-16/...path. The store keys by the bare relpath (matching whatbridge_eval_inline.py/closure_convergence_sweep.pywrite). Grepping with thecommons/journals/prefix returns ABSENT even for journals that ARE present, which mis-diagnoses a clean re-detection as a genuine gap and can trigger spurious re-bridging. Confirmed 2026-07-16 residual-gap closure: a prefix-inclusive grep reported ABSENT; the bare-relpath grep (and the closure scripts) confirmed the journal was already in both stores. Always usegrep -c 'ocas-mentor/2026-07-16/<file>.json'(no leading dir) for manual verification; the closure scripts (verify_genuine_gap_profile.py,bridge_eval_inline.py) parse JSON and never hallucinate key-format gaps, so prefer them over raw grep. - Placeholder-then-patch anti-pattern — never write placeholder strings intending to fix later
- Heredoc
$(date)timestamp mismatch — compose TS into a variable first, use for both filename and content - Research is not optional for improvements — MUST run Phase 1.5 research before touching files
forge.updatevsgit pulldivergence — cross-check git log against SKILL.md version fieldforge_audit_skills.pyis now functional (corrected 2026-07-14) — The script atscripts/forge_audit_skills.pyis a REAL compliance audit: it scans everyocas-*skill'sscripts/for forbidden non-secret env-var config reads (GENIE_*,<NAME>_MAX_AGE_DAYS,<NAME>_PATH,<NAME>_ENABLED, etc.) and itsSKILL.mdbody for env-var config tables, and exits non-zero on any blocking (ERROR) issue. Runpython3 scripts/forge_audit_skills.py(or--skill ocas-<name>for one). It skips.bakfiles. Whenforge.auditis invoked, run this first — it is the standing gate, not a manual-only task. The pre-2026-07-14 stub note is obsolete.nargs='[]'is invalid in argparse — When defining command-line arguments,nargs='[]'is not a valid option and will cause a ValueError. To accept zero or more arguments, usenargs='*'instead. This caused the run_dispatch_pipeline.py script to fail until corrected.- Tool-layer flake:
skill_view/read_file/write_file/search_filesall returnDaemonThreadPoolExecutorAPI error (observed 2026-07-16 dispatch + this session's mixed-wave closure) — When loading a skill (skill_view), reading aSKILL.md/ journal JSON (read_file), OR writing a file (write_file), the tool layer can intermittently fail withError during OpenAI-compatible API call #N: 'DaemonThreadPoolExecutor' object has no attribute '_initializer'.terminal()is NOT affected. Read fallback: read the same file directly withterminal()— usefind <dir> -name '*.md'/-name '*.json'to locate it, thencat <path>. Write fallback: useterminal()cat > file << 'EOF'heredoc (JSON) orcat > script.py << 'PYEOF'heredoc — the same pattern ocas-mentor/ocas-praxis already mandate for their cron JSON/journal writes. Avoid${...}/$(...)inside heredocs (bash expansion pitfall) and watch for the Praxis double-Z filename trap. Search fallback: oncesearch_filesflakes, route around it with terminalfind <dir> -name '*.py'(file search) orgrep -rn 'PATTERN' <dir>(content search) — do NOT burn 3+ retries. Do NOT burn 3–4 retries onskill_view/write_file/search_files; after 1–2 identical failures, switch to terminal. When the flake hits MULTIPLE skills in one run, don't retry each individually — batch-read ALL remaining needed SKILL.md files in ONE terminalcat(e.g.cd skills && for s in ocas-mentor ocas-praxis ocas-dispatch; do echo "== $s =="; cat $s/SKILL.md; done) instead of burning Nskill_viewretries; the 2026-07-16 dispatch wave closed a combined re-detection this way afterskill_viewsucceeded once then failed 3× in a row. CRITICAL non-determinism: the flake is NOT consistent even within one session —write_filesucceeded for several/tmp/*.pywrites earlier in 2026-07-16 closure, then failed twice in a row on a later/tmp/update_state.py+last_email_check.jsonwrite before theterminal() cat >fallback succeeded. So neither a prior success nor a prior failure predicts the next call; route around it on first flake. Skill files live under~/.hermes/profiles/indigo/skills/<name>/and journals under~/.hermes/profiles/indigo/commons/journals/<skill>/<date>/— the filesystem path is authoritative. The flake is in the tool layer, not the skill content; treat it as transient and route around it. (Confirmed live 2026-07-16:skill_view×3 andread_file×4 all failed identically;terminal() cat/cat >/find/grepsucceeded on every attempt. The flake had cleared by the review pass, so it is genuinely intermittent, not persistent.)
The Forge build pipeline:
- Existence gate — check if skill already exists
- Research — search GitHub, arxiv, skill registries for patterns
- Classify — determine skill type and scope
- Scope — define boundaries and interfaces
- Architecture — design the package structure
- Plan — create implementation plan
- Build — implement the skill
- Validate — run critique and verify 50/50
Forge reads variant proposals and decisions from Mentor journals.
File types received:
{proposal_id}.json— VariantProposal{decision_id}.json— VariantDecision
After processing each file, move to the consumer's ingestion log.
See references/interfaces.md for full handoff contracts.
See references/storage-layout.md.
See references/okrs.md.
- Critique (skilllab) — the evaluation complement to Forge's build pipeline
- Mentor — receives VariantProposal and VariantDecision files via journal payload
- Fellow — Forge may build experiment harnesses for Fellow benchmarks
- Custodian — initializes skills built by Forge during system health passes
- Chronicle — skill metadata and journal entries ingested for knowledge persistence
Action Journal — every build, critique, repair, validation, audit, and variant processing run.
On first invocation of any Forge command, run forge.init. Creates data
directories, writes default config, registers the forge.update cron job, and
logs the initialization decision. See references/init_procedure.md for the
exact sequence.
When triggered by dispatcher or forge:journal-scan cron, run these steps (full operational detail + all recovery/closure gotchas in references/dispatch-integration-detail.md; canonical decision procedure in references/dispatch-pipeline-guide.md):
- Check for unprocessed
vp_*.json/vd_*.jsonin data root,proposals/,intake/ - Cross-reference against
intake/processed/andprocessed/to skip already-processed files - Process new files: build variants, apply fixes, queue for Mentor
- Move processed files to
processed/ - If no unprocessed files: write no-op journal and exit
- Perform phantom file cleanup: after every dispatch run,
lsjournal dirs and fix empty/double/malformed timestamps (seereferences/phantom-file-cleanup.md)
Critical dispatch rules (all detailed in the references file): explicit-run prompt overrides the no-op shortcut; second-wave = add gaps + advance state, never write new journals; bridge all output journals into BOTH eval stores idempotently by full relative path (never basename); advance last_ingest_run past max mtime of ALL touched journals INCLUDING post-dispatch mentor-cron heartbeats, then RE-SWEEP to GENUINE GAP = 0; recoveries REWRITE the existing wave journal (never mint a new one); for post-state closure, iterate scripts/closure_convergence_sweep.py --date <DATE> to 0 additions then assert GENUINE GAP = 0 via scripts/verify_genuine_gap_profile.py --date <DATE> (both confirmed present on disk 2026-07-15; the prior note that reconcile_*/verify_*/closure_* helpers "do NOT exist on disk" is obsolete for these two — see references/dispatch-closure-sequence.md). NEVER rm -rf the home/ tree — only the doubled home/.hermes node.
Monitor gate state is a SEPARATE file — and it has TWO copies (confirmed 2026-07-16 closure): the journal re-fire is gated by monitor_journals.py reading the PROFILE-relative monitor state at ~/.hermes/profiles/indigo/commons/data/monitor_state/journal_ingest_state.json (latest_mtime) — NOT ocas-praxis/ingest_state.json. Closing a re-detection MUST advance BOTH monitor copies AND the praxis last_ingest_run, all past the max journal mtime. Always json.load+overwrite BOTH copies (/root/.hermes/commons/data/monitor_state/journal_ingest_state.json and /root/.hermes/profiles/indigo/commons/data/monitor_state/journal_ingest_state.json). CORRECTED 2026-07-17: scripts/closure_closeout_check.py (an ocas-FORGE script, lives only in ocas-forge/scripts/, NOT ocas-dispatch) was updated to read BOTH monitor copies and to assert gate [2] against the profile copy (the load-bearing one); the old version read only the root copy and gave a false "closed". The verifier's gate [2] reporting True IS now proof the monitor is satisfied (both copies checked). Email gate fix (2026-07-17): the verifier no longer requires the two top-level GWS-snapshot files (last_email_check.json, last_email_check_jared_zimmerman_gmail_com.json) — those stay null under the monitor re-fire bug and are the known un-closeable gate; it now REQUIRES only the dispatch-owned account copies (jared/last_email_check.json, last_email_check_jared.json, indigo equivalents) and WARNs on the top-level snapshots. See references/redetection-stale-state-closure-oneshot.md (Monitor gate state section).
- Never recursive-glob
commons/journals/**/*.jsonduring closure diagnosis (confirmed 2026-07-16): a symlink/dir loop nestscommons/journals/journals/journals/...to arbitrary depth, so a recursive**/glob over the journals tree returns millions of duplicate paths and can dump 30+ MB into one tool result, burying the real answer. The closure scripts (closure_convergence_sweep.py,verify_genuine_gap_profile.py) already use a BOUNDED per-skillos.listdir(skill)/<DATE>walk — copy that pattern. For ad-hoc discovery, globcommons/journals/<skill>/<DATE>/*.jsonper skill, never**. - Re-detection recurs because the wave writer doesn't advance gates atomically (07-13→07-14→07-15→07-16 all re-fired): each
dispatch-wave-*.jsonis written but the monitorlatest_mtime+ praxislast_ingest_runare left at pre-wave values, somonitor_journals.pyre-enqueues the same files next cycle until a manual closure runs. Real fix: have the wave writer advance both gates in the same transaction as the journal write (or invoke the closure Closeout at wave end). Manualclosure_closeout_check.py+ dual-copy state advance is the stopgap, not the cure — note it in any closure journal so the recurring pattern is visible. Mtime-advance truncation trap (confirmed 2026-07-16 closure): when advancing these gates, NEVER hand-type the float/ISO mtime literal — a hand-typed1784232657.4915TRUNCATES below the true1784232657.4915047(4.7e-6 under), sostate < max_journal_mtime,closure_closeout_check.pyreportsmonitor>=max: False, and the dispatcher RE-FIRES forever. This is the SAME failure class asredetection-epoch0-pitfall.md(state < max) but a DIFFERENT cause (hand-typed literal truncation vs. bad arithmetic on a 0-gap sweep). ALWAYS recomputemax(os.path.getmtime(p) for p in glob('commons/journals/*/<DATE>/*.json'))programmatically and add>= 1.0spad so any heartbeat landing during the write is also covered. Seereferences/redetection-mtime-truncation-pitfall.md. - Scheduled dispatcher RE-FIRES mid-closure and CLOBBERS the two top-level GWS-snapshot email-state files (confirmed 2026-07-16T2145Z): a manual Mode-C closure re-flagging
verified_second_wave=Trueoncommons/data/ocas-dispatch/last_email_check.json+last_email_check_jared_zimmerman_gmail_com.jsongot overwritten ~60s later when the scheduleddispatcher.py/monitor_email.pyfired AGAIN during the run, rewriting those two files as pure gws snapshots (rawnew_threads) withverified_second_wavedropped to Null. The dispatch-OWNED jared copies (jared/last_email_check.json,last_email_check_jared.json) survive intact (True). Mitigation: after re-flagging, immediately re-runscripts/closure_closeout_check.pyin the SAME script (subprocess) so the verifier reads the flag before the next dispatcher tick (minimize clobber window); if[3]still shows None, the dispatcher landed between your write and the verifier — just re-flag the two top-level files and re-verify. Do NOT chase a permanently-green[3]on those two files; they are the known un-closeable gate under the monitor re-fire bug (seereferences/dispatch-wave-email-state-topology.md, Caveat). The LOAD-BEARING gates are[1](named journal in both eval stores) and[2](state advanced past max mtime), which DO stay green once advanced. Full recipe inreferences/closure-email-state-refire-pitfalls.md. - Recursive
**/last_email_check*.jsonglob OVER-PERTAINSindigo/last_email_check.jsonduring a single-account closure (confirmed 2026-07-16T2145Z): that file's basename islast_email_check.json(identical to the jared top-level snapshot), so a blanket recursive glob + flip-to-True wrongly set indigo'sverified_second_waveto True, corrupting indigo's genuinefalsestate (it was a realaction:nonetriage from 20260716T161444Z, not second-wave). This is the REVERSE of thedispatch-wave-email-state-topology.md"key on path not account" advice. Mitigation: during a jared-specific closure, target jared's files by EXPLICIT paths only —last_email_check.json,last_email_check_jared_zimmerman_gmail_com.json,jared/last_email_check.json,last_email_check_jared.json— never a recursive**/last_email_check*.jsonblanket. If you over-flip, restore the other account's file from its prior true value. Full detail inreferences/closure-email-state-refire-pitfalls.md.
The expanded critical dispatch rules, recovery recipes, and the full 7-pattern eval-gap catalog are in references/dispatch-integration-pitfalls-skillmd.md — read it before running any dispatch pipeline or recovery. It is the companion to the summary above and the canonical references/dispatch-pipeline-guide.md.
forge.update pulls the latest package from the source: URL in frontmatter.
Runs silently unless version changed or error.
Drift detection procedure (added 2026-06-23, extended 2026-06-29): Two distinct drift scenarios exist — handle BOTH:
Scenario A — Origin ahead (upstream has new commits): Dev installs can accumulate local commits that diverge from origin, causing git pull to falsely report "Already up to date." Before declaring up-to-date: (1) git fetch origin, (2) git log --oneline HEAD..origin/main — any output = upstream ahead, (3) git diff --stat origin/main — any diff = content drift regardless of commit history, (4) cross-check SKILL.md version: field against git log origin/main --oneline -1 — frontmatter can lag commit messages. See Gotchas for conflict resolution when local modifications contradict upstream direction.
Scenario B — Local drift (working tree dirty, origin NOT ahead): When git log HEAD..origin/main is EMPTY but git diff --stat origin/main shows changes, OR when git status lists untracked files, the local working tree has accumulated modifications that origin doesn't have. This commonly happens when: session journals are written into references/, SKILL.md gets edited locally without committing, or new support files are added but not pushed. Diagnostic: git status --short — if output is non-empty with origin at same commit, you have local drift. Response: (1) Separate operational artifacts (session journals in references/) from skill content changes (SKILL.md edits, new reference docs). (2) Session journals should be moved to commons/data/ocas-forge/journals/ — they do NOT belong in the skill repo. (3) Skill content changes should be committed and pushed, or stashed if experimental. (4) NEVER git add . blindly — this commits operational logs into the skill's git history. Confirmed 2026-06-29: 100+ session journals accumulated in references/ over 7 days, causing git diff --stat to show 100+ untracked files with origin at same commit.
forge.consolidate merges an orphan or duplicate skill into its natural parent.
See references/builder_workflows.md for the full workflow.
Core rule: fold merged content into the parent's existing section structure.
Do NOT wrap in ## Integrated: sections.
Consolidation hazard — a SKILL.md-less ocas-* dir is NOT automatically a removable orphan (confirmed 2026-07-13): When auditing for orphans, finding a directory named ocas-* or util-* WITHOUT a SKILL.md does NOT mean it is safe to delete. It may be live state storage hard-coded as a STATE_FILE (or similar) by a sibling skill. Before removing any such dir: (1) grep -rln "skills/<name>" . --include=*.py --include=*.json across the whole tree — if any script references the path, it is load-bearing; (2) check the dir's contents' mtime — a recently-modified *.json inside means a running cron writes there. Real case from this audit: ocas-critique/ had NO SKILL.md (already merged into ocas-skilllab, confirmed by merged-from: ocas-critique in skilllab's frontmatter) yet ocas-skilllab/scripts/critique_10khr_runner.py hard-codes STATE_FILE = os.path.join(_HERMES_ROOT, "skills", "ocas-critique", "commons", "data", "ocas-critique", "10khr-state.json"), and that file's mtime was 2026-07-12 (written by a cron) — deleting the dir would have broken the 10khr engine. Contrast the genuine orphan found same session: ocas-10xeng-autofix/ (top-level) had no SKILL.md, only a stale last_run.json dated 2026-07-01, ZERO path references anywhere, and a real canonical copy at software-development/ocas-10xeng-autofix/ — safe to move to .archive/. Decision rule: only remove a SKILL.md-less ocas-* dir after BOTH grep-for-path returns no script/config references AND the dir's contents are not recently written by a cron. Session detail: references/session-20260713-audit-orphan-state-storage.md.
Before creating any GitHub repo, verify the skill is OCAS-authored. See
references/github_repo_guardrails.md for the full guardrail checklist.
When Phase 1.5 triggers skill library research, use these APIs to understand approaches and patterns — not to copy verbatim.
- SkillsMP —
sk_liv...370Ikey, https://skillsmp.com/docs/api - AgentSkill.sh —
agentskillCLI, https://agentskill.sh - LobeHub —
lobehubCLI, https://lobehub.com/cli - Skills.sh — https://www.skills.sh/docs/api
- OpenClaw —
clawhubCLI, https://docs.openclaw.ai/clawhub/cli - GitHub OCAS repos —
gh search repos "ocas-* user:indigokarasu" --json fullName,description,url
Output of library search: For each relevant skill found, note: name, description, how it works (architecture), what patterns it uses, and what the new skill can learn from it. Synthesize — don't clone.
When asked to audit sync state of all OCAS skills, or when running a scheduled
sync check, use the workflow in references/sync_audit_procedure.md.
forge.audit audits one or more existing OCAS skills for architecture compliance,
applies fixes, and syncs to GitHub.
Mandatory Configuration Policy gate (blocks submission to the Nous optional-skills catalog): behavioral settings (thresholds, retention windows, feature flags, display prefs, paths) MUST NOT be read from environment variables — the hermes-sweeper auto-closes such PRs under the env-var-for-config policy. Correct mechanism: declare each setting in metadata.hermes.config, read it at runtime from $HERMES_HOME/config.yaml under skills.config.<key> (via PyYAML — telephony.py is the reference impl), document skills.config.<key> in SKILL.md (never env-var names), and let CLI flags override. Only secrets go in .env; only HERMES_HOME/HERMES_PROFILE locate the runtime.
Run the automated check first:
python3 scripts/forge_audit_skills.py --skill <ocas-name> # 0 exit = cleanIt flags any GENIE_* / non-secret env-var config read in scripts/ or env-var config table in SKILL.md. The full written standard lives in ocas-skilllab's references/nous-skill-requirements.md (Configuration Policy section) and references/compliance-audit-checklist.md.
Forge uses the memory tool lightly — only for build state during multi-step builds.
| File | When to read |
|---|---|
references/design_pipeline.md |
Before forge.build — the mandatory 8-phase pipeline |
references/init_procedure.md |
On first invocation of any Forge command |
references/sync_audit_procedure.md |
Before forge.sync-audit |
references/package_patterns.md |
Before structuring a new skill package |
references/authoring_rules.md |
Before writing or editing any skill SKILL.md |
references/builder_workflows.md |
Before forge.verify-update, forge.consolidate, forge.sync, or forge.audit |
references/github_repo_guardrails.md |
Before creating any GitHub repo or PR |
references/synthesis-methodology.md |
When synthesizing a new skill from multiple source skills |
references/schemas.md |
Before creating or validating data structures |
references/storage-layout.md |
When debugging data path issues or managing disk |
references/naming-and-authorship.md |
Before naming, renaming, or setting author on any skill |
references/journal-scan-cron-guide.md |
Before running forge:journal-scan — cron mode procedures |
references/frontmatter-editing-pitfalls.md |
Before editing any skill frontmatter |
references/dispatch-pipeline-guide.md |
Multi-skill dispatch workflow (Forge+Mentor+Praxis) — genuine vs second-wave decision procedure, 7 eval gap patterns, cross-directory relpath false positive, journal writing standards, third-wave mitigation, post-dispatch cleanup, phantom file prevention. CONSOLIDATES 60+ dispatch patterns. Load BEFORE running any dispatch pipeline. |
references/dispatch-integration-pitfalls-skillmd.md |
Dispatch integration inline detail — expanded critical dispatch rules, recovery recipes, and the full 7-pattern eval-gap catalog extracted from SKILL.md. Read BEFORE running any dispatch pipeline or forge:journal-scan cron run (companion to dispatch-pipeline-guide.md). |
references/dispatch-bridge-rerun-pitfalls.md |
Caller-side bridge re-run pitfalls — duplicate-journal-on-rerun (later-turn timestamp collision), phantom-guard None journal_id crash, and the split write/bridge fix. Read after any dispatch bridge run that writes its own journals. |
references/stale-proposal-backlog.md |
Before scanning proposals — handling stale unprocessed proposals |
references/journal-file-path-construction.md |
Before writing any journal — correct path pattern with date subdirectory, recovery from misplaced files |
references/phantom-file-cleanup.md |
After writing journals via terminal() — empty timestamps, double timestamps, malformed filenames |
references/interactive-menu.md |
When invoked interactively via / command — two-level menu layout, response parsing, platform adaptation |
references/session-20260713-dispatch-explicit-run.md |
Dispatch 2026-07-13: Explicit-run override verified end-to-end. Caller-side bridge recipe (Forge no-op + real Mentor heartbeat + real Praxis ingest → bridge to BOTH eval files). Confirms the pipeline scripts do NOT write ingest_state.json, so the caller must advance last_ingest_run + resync counters. |
references/session-20260714-dispatch-recovery.md |
Dispatch 2026-07-14 (TWO recovery passes): (1) Prior-wave-misclassification RECOVERY — do NOT run bridge_explicit_run.py (mints a 2nd wave journal, re-fires dispatcher); hand-run pipeline, rewrite EXISTING dispatch-wave (same run_id). (2) Plain re-detection closure — EXCEPTION pre-flight false-negative when wave emits forge-<TS>.json (no -scan); mentor-cron heartbeat convergence loop (re-sweep to 0 gaps). |
references/recover-dispatch-wave.md |
Verbatim recovery command sequence for the prior-wave-misclassification case: confirm trigger, caller-side bridge (Forge + Mentor + Praxis), rewrite EXISTING wave journal, then MANDATORY reconcile_dispatch_eval_today.py --apply + verify_genuine_gap_profile.py gap assertion. Read this instead of reconstructing the steps from the SKILL.md bullet. |
references/session-20260714-dispatch-1240Z-forge.md |
Dispatch 2026-07-14T12:40Z: Explicit-run override fires even when named new_file already evaluated — NEW post-prior-wave cron heartbeat requires full pipeline + bridge. Pitfalls: malformed wave filename from truncated TS; legacy bare-filename eval entries are NOT phantoms. |
references/dispatch-recovery-gap-reconciliation.md |
Recovery gap pitfall: gap_backfill.py gives a FALSE 0 during recovery (mtime-lag masks real dispatch-eval gaps). Use the two-store on-disk reconciliation pattern instead. |
references/session-20260713-audit-orphan-state-storage.md |
Audit 2026-07-13: SKILL.md-less ocas-* dir is NOT automatically removable — may be live STATE_FILE storage for a sibling skill. The orphan-vs-load-bearing decision rule for forge.consolidate/forge.audit. |
scripts/bridge_explicit_run.py |
Caller-side bridge for explicit-run new_journals-only dispatch waves. Runs Forge scan + real Mentor heartbeat (subprocess stdin, not shell pipe) + idempotent dual-eval bridge + dispatch-wave journal + state advance in one atomic run. USE THIS INSTEAD of the broken run_dispatch_pipeline.py for pure new_journals explicit-run overrides. INCOMPLETE for MIXED waves (confirmed 2026-07-15): it does NOT run the Praxis ingest (ocas-praxis/scripts/praxis_ingest_run.py --mode dispatch) and does NOT re-affirm the email second-wave state file (commons/data/ocas-dispatch/<acct>/last_email_check.json verified_second_wave). For a mixed new_journals+new_emails explicit-run wave, run the full pipeline by hand per references/session-20260715-mixed-wave-closure.md — or extend this script. All timestamps composed once; phantom-entry guard via on-disk existence check. TWO WAVE JOURNALS EXPECTED (confirmed 2026-07-16): when the email pass runs separately (genuine OR second-wave), it writes its OWN dispatch-wave-*.json — so a single mixed dispatcher fire legitimately yields TWO wave journals: one from this script (at the forge-scan TS) and one from the email triage (at its own TS). Both must be bridged into BOTH eval stores (commons/data/ocas-praxis/journals_evaluated.jsonl + commons/data/ocas-dispatch/journals_evaluated.jsonl). This is NOT a duplication defect and the second journal is NOT a phantom — do not purge it in closure. |
scripts/bridge_eval_inline.py |
Working idempotent dual-store eval bridge (created 2026-07-15). Appends relpaths to BOTH praxis-eval (journal_id) and dispatch-eval (filename) stores, skipping present entries. Corrected --action handling (value consumed, never treated as a relpath). Use --require-exists to skip relpaths whose file is missing on disk (prevents phantom eval entries). Usage: python3 scripts/bridge_eval_inline.py REL1 REL2 --action my_label. Fills the gap left by the long-documented-but-missing bridge_eval_inline.py. |
scripts/verify_genuine_gap_profile.py |
Working bounded per-skill os.listdir two-store reconciliation — replaces the phantom verify_genuine_gap_profile.py/reconcile_dispatch_eval_today.py the doc once referenced. Assert GENUINE GAP=0 before declaring a dispatch-wave closure. UNGATED (walks ALL today-dated journals, no mtime filter). |
scripts/closure_convergence_sweep.py |
Working ungated two-store BRIDGE (not just report) that mirrors verify_genuine_gap_profile.py's walk and appends any missing journal into the store(s) it is absent from. Run iteratively (loop until it adds 0) immediately BEFORE the verify assertion. Exits 1 while it still bridges gaps, 0 when stable. |
scripts/closure_closeout_check.py |
Close-out verifier (added 2026-07-16, CORRECTED 2026-07-17): asserts ALL closure gates in one pass — named journal in both eval stores, both state gates (praxis last_ingest_run + monitor latest_mtime) >= max today-journal mtime, and dispatch-owned email verified_second_wave flags. Replaces the inline hardcoded close-out heredoc in references/redetection-stale-state-closure-oneshot.md. Run python3 scripts/closure_closeout_check.py --named <rel> --date <DATE>; exits 1 if any gate is stale. NOTE this is an ocas-FORGE script — invoked from ocas-forge, NOT ocas-dispatch (skills/ocas-dispatch/scripts/closure_closeout_check.py does NOT exist; confirmed 2026-07-17). 2026-07-17 correction: (a) reads BOTH monitor copies — root /root/.hermes/commons/data/monitor_state/journal_ingest_state.json AND profile-relative /root/.hermes/profiles/indigo/commons/data/monitor_state/journal_ingest_state.json (the profile copy is what monitor_journals.py actually gates on; the prior version read only the root copy and falsely reported "closed" while the profile copy stayed stale, re-firing the wave); (b) REQUIRES the dispatch-owned account copies (jared/last_email_check.json, last_email_check_jared.json, and the indigo equivalents) and only WARNs on the two top-level GWS-snapshot files (last_email_check.json, last_email_check_jared_zimmerman_gmail_com.json) which stay null under the monitor re-fire bug — the prior version required those and so every Jared closure failed gate [3] regardless of real state. |
scripts/verify_eval_no_phantoms.py |
Detector for phantom eval entries (journal_id pointing at a non-existent file). Bounded/current-wave scope preferred over unscoped historical --fix. |
scripts/run_dispatch_pipeline.py |
Broken (argparse nargs='[]', no dispatch-eval bridge). Consult only for routine second-wave; use bridge_explicit_run.py for explicit-run overrides. |
references/session-20260715-mixed-wave-closure.md |
Mixed explicit-run wave closure (2026-07-15): A dispatcher fire carrying BOTH new_journals (explicit-run override) AND new_emails (all is_new:false = email second-wave). Verified caller-side sequence: run Forge scan + real Mentor heartbeat + Praxis ingest (which bridge_explicit_run.py omits), bridge all outputs into BOTH eval stores, advance ingest_state.last_ingest_run, re-affirm email verified_second_wave via full-file write_file, then post-dispatch mentor-cron convergence sweep → assert GENUINE GAP=0. Includes the manual eval-append fallback (since bridge_eval_inline.py is missing). |
references/mixed-wave-closure-one-shot.md |
One-shot closure runbook — the runnable /tmp/run_pipeline.py orchestration pattern (timestamps-once, Forge→Mentor→Praxis→bridge→state→email→sweep→verify) that actually closed a live mixed wave, PLUS the dispatch-wave phantom-purge gap the closure walk excludes. Read alongside session-20260715-mixed-wave-closure.md. |
references/mixed-wave-preflight-triage.md |
Mixed-wave PRE-FLIGHT triage (2026-07-15): DECIDE which mode BEFORE touching anything. Copy-pasteable read-only Python pre-flight that distinguishes Mode A (fresh explicit-run → mint new wave journal), Mode B (prior-wave-misclassification recovery → REWRITE existing wave journal, same run_id, run genuine pipeline, advance state), Mode C (re-detection closure → no re-run). Run this FIRST — it tells you whether session-20260715-mixed-wave-closure.md is a full-write or a rewrite path. |
- Mixed-wave RE-DETECTION closure (closure-only, 2026-07-15; email-evidence gate added 2026-07-17): When a LATER wave already fully processed the same
new_files+ email threads — do NOT re-run pipelines or mint a wave journal. MANDATORY pre-close email gate:closure_closeout_check.pygate [3] (verified_second_wave: Trueon dispatch-owned files) is NOT proof of work — the flag can be a false positive from a prior wave that asserted verification without producing it (email-evidence-verification-gap failure, 2026-07-14). Before re-affirming or advancing state, runpython3 skills/ocas-dispatch/scripts/verify_evidence_threads.py --evidence commons/data/ocas-dispatch/evidence.jsonl <thread_id...>on every dispatch thread in the wave; any thread printingNOT_IN_EVIDENCEorin_evidence(structured)with NOaction=token is a genuine Path B gap — triage it (perocas-dispatchreferences/cron-triage-workflow.md) BEFORE closing. Only after evidence is confirmed present withaction=tokens, continue: bridge residual one-sided gaps (manual dual-store append;bridge_eval_inline.pyabsent), run the continuous mentor-cron convergenceos.listdirre-sweep until 0 additions, advancelast_ingest_runvia full-filejson.load+write_file, re-affirm emailverified_second_wave, then assertGENUINE GAP=0. Distinguishes re-detection from prior-wave-misclassification recovery (which rewrites the existing wave). Seereferences/mixed-wave-closure-email-evidence-gate.md. |references/redetection-stale-state-closure-oneshot.md| MODE C re-detection stale-state closure — one-shot runbook (2026-07-16): Date-agnostic copy-paste pre-flight + closure sequence for the PURE stale-state loop (journal already in BOTH eval stores). Covers BOTHlast_ingest_run < named-journal-mtime(named file is the gap) ANDlast_ingest_run == named-journal-mtime(named file is bridged; LATER mentor-cron heartbeats are the real gap — re-fire is NOT a misclassification). Includes the mandatory POST-ADVANCE re-sweep loop (a mentor-cron heartbeat lands between the pre-advance sweep and the state write), thejson.load-not-read_fileguard, the REAL eval-store paths (commons/data/ocas-*/journals_evaluated.jsonl, NOTcommons/journals/ocas-dispatch/...), and the email second-wave re-affirm block (incl. the account-mislabel pitfall — a Jared file carryingaccount: mx.indigo.karasu@gmail.comORaccount: null; the overwrite guardif s.get("account") != exp_acctcatches BOTH — confirmed 2026-07-16T19:52Z). For the final gate assertion, preferscripts/closure_closeout_check.py --named <rel> --date <DATE>over the inline heredoc. Use this for a plainnew_journalsre-fire that is NOT a mixed wave. | |references/mixed-wave-closure-email-evidence-gate.md| Mixed-wave closure email-evidence gate (2026-07-17): Theclosure_closeout_check.pygate [3]verified_second_wave: Trueis NOT proof of email work (2026-07-14 false-positive incident). Before re-affirming/advancing state on ANY mixed wave, runverify_evidence_threads.pyon every dispatch thread; only close when all printin_evidence(structured) action=.... Full command, authoritative evidence path, and the 2026-07-17 confirmation (10 Jared threads all genuinelyaction=none). | |references/dispatch-explicitrun-closure-recipe.md| Explicit-run new_journals dispatch recipe (2026-07-15): Copy-pasteable pre-flight (a/b/c) decision tree + genuine-run + combined-wave repair + closure steps for an explicit-runnew_journals(optionally+new_emails) wave. Includes the FALSE "stalelast_ingest_run" diagnosis pitfall (owndispatch-wave-*.jsonexcluded from closure mtime) and the cronexecute_code-block workaround. | |references/dispatch-closure-sequence.md| Post-state closure sequence (2026-07-16): exact caller-side commands afterbridge_explicit_run.py— iterateclosure_convergence_sweep.pyto 0 additions, then assertverify_genuine_gap_profile.pyGENUINE GAP=0. Confirms both scripts exist on disk (obsoletes the "do NOT exist on disk" note). | |references/dispatch-wave-email-state-topology.md| Email-state file topology (2026-07-16): the 7 email-state files undercommons/data/ocas-dispatch/, which 4 the closeout verifier actually inspects, and why re-affirmverified_second_wavevia a full glob (key on path, not the often-nullaccountfield). | |references/closure-email-state-refire-pitfalls.md| Closure email-state pitfalls (2026-07-16T2145Z): (A) scheduled dispatcher re-fires mid-closure and clobbers the two top-level GWS-snapshot files'verified_second_wave— re-flag + re-verify in one script; (B) recursive**/last_email_check*.jsonover-pertainsindigo/last_email_check.jsonduring a jared closure and corrupts indigo's state — use explicit per-account paths. Companion todispatch-wave-email-state-topology.md. | - Re-detection closure PITFALL (2026-07-15): a no_op/second-wave closure that bridges siblings but SKIPS advancing
last_ingest_runpast the processed file mtimes leaveslast_ingest_runBELOW the files → dispatcher re-fires the SAME files every ~5 min forever. Closure MUST advancelast_ingest_runto the MAX mtime across ALL today's journals (incl. mid-run mentor-cron heartbeats). CRITICAL sub-trap:dispatch_redetection_close.pyonce advanced state to epoch 0 on a 0-gap re-sweep — seereferences/redetection-epoch0-pitfall.mdfor the bug, the fix, and the mandatory post-closure state-verification discipline (GENUINE GAP=0does NOT prove state was advanced). - Closure script does NOT advance the monitor
latest_mtimecopies (CONFIRMED LIVE 2026-07-17):dispatch_redetection_close.pyadvances ONLYingest_state.last_ingest_run(the praxis copy). It does NOT touch the two monitorjournal_ingest_state.jsoncopies (/root/.hermes/commons/data/monitor_state/root +/root/.hermes/profiles/indigo/commons/data/monitor_state/profile-relative). Gate [2] inclosure_closeout_check.pyreads BOTH monitor copies, so after the closure script runs, gate [2] stays False and the wave re-fires forever. MANDATORY extra step after any closure run: recomputemax_mt = max(os.path.getmtime(p) for p in glob of ALL today-journals EXCLUDING ocas-custodian + dispatch-wave-*), setnew = max_mt + 2.0(≥2s pad, NEVER hand-typed literal — seeredetection-mtime-truncation-pitfall.md), andjson.load+overwrite BOTH monitor copies withlatest_mtime = new. Then re-runclosure_closeout_check.pyand confirm=== gates ALL CLOSED ===. This applies even when you ran the genuine pipeline viabridge_explicit_run.py(which also leaves monitor copies stale). CONFIRMED LIVE 2026-07-17: a genuine-run wave (bridge_explicit_run.py + dispatch_redetection_close.py) left gate [2] False (monitor copies at 06:40:41Z vs max journal 06:48:43Z) until both copies were advanced by hand. - Pre-flight must scan ALL today-journals, not just the dispatcher's
new_files(CONFIRMED LIVE 2026-07-17): the dispatcher flags only files present at itsdetected_at. A journal that LANDS AFTER detection (mtime > detected_at) but is missing from BOTH eval stores is GENUINE work even when the dispatcher-named file is already bridged (a re-detection). Thedispatch-explicitrun-closure-recipe.mdpre-flight (a/b/c) checks only the namednew_files; extend it with a (d) step:globeverycommons/journals/<skill>/2026-07-17/*.json, check each against BOTH eval stores; if ANY today-journal is absent from either store, run the GENUINE pipeline (do NOT closure-only). CONFIRMED LIVE 2026-07-17: wave detected at 06:40:37Z flagged063755Z(already in both stores = re-detection), but064536Zlanded at 06:45:36Z AFTER detection and was genuinely missing from both stores. Scanning only the named file would have skipped the real gap. |scripts/forge_audit_skills.py| Functional OCAS compliance audit (2026-07-14): scans allocas-*skills for forbidden non-secret env-var config reads + env-var config docs; exits non-zero on violations. Run before any submission. | |scripts/forge_count_unprocessed.py| Safe bounded count of unprocessed Forge variant proposals (vp_*/vd_*). Walks ONLYcommons/data/ocas-forge/intake/, excludingintake/processed/, theproposals/mirror, and top-levelprocessed/. Prints the integer count. USE INSTEAD of any recursivefind/os.walkover the whole tree (which overcounts both mirrors and falsely flipsroutine_no_op→genuine). Re-confirmed 2026-07-16. | |scripts/update.sh| Local update helper. | |references/redetection-mtime-truncation-pitfall.md| MODE C mtime-advance truncation trap (2026-07-16 closure): when advancing monitor latest_mtime + praxis last_ingest_run, NEVER hand-type the float/ISO literal — it truncates below the true max journal mtime and silently leaves state < max, so the verifier reports False and the wave re-fires forever. Recompute max(os.path.getmtime(...)) programmatically + >=1s pad. Re-fires-forever = same class as redetection-epoch0-pitfall.md but different cause (literal truncation, not bad arithmetic). | |references/closure-post-ingest-mtime-trap.md| Post-ingest caller-journal mtime trap (2026-07-17): the Praxis ingest template setslast_ingest_runto its OWN NOW mid-pipeline, so caller-writtenpraxis-cron-*.json+dispatch-wave-*.json(later mtimes) exceed it — verifier gate [2] praxis-half goes False even whenGENUINE GAP=0, re-firing forever. After writing those journals, RE-ADVANCElast_ingest_runpast max today-journal mtime (+>=5s pad), then re-sweep/verify/closeout. Distinct cause from the hand-typed truncation trap above. |