Skip to content

feat: exclusive-access gate -- refuse WS/CF schema changes while FLEx holds the project - #343

Merged
MattGyverLee merged 20 commits into
mainfrom
feat/exclusive-access-gate
Oct 2, 2026
Merged

MattGyverLee merged 20 commits into
mainfrom
feat/exclusive-access-gate

Conversation

@MattGyverLee

@MattGyverLee MattGyverLee commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Summary

Shared mode lets the MCP write while FieldWorks has a project open. Two kinds of change are still unsafe from a peer:

  • writing-system changes crash the FLEx that holds the project (seen live);
  • custom-field definitions are silently lost (from LCM source).

This PR adds the refusal that shared mode was missing, plus the follow-ups around it. Spec: specs/exclusive-access-gate/ (successor to #93).

  • New error requires_exclusive_access. It refuses a write-enabled run_module whose script makes such a change when the probe says open_shared or unknown. The refusal comes before confirmation, backup and subprocess. open_exclusive / held_by_other still give project_locked, and read-only runs and ordinary edits are never gated.
    • Detection reuses the read-only certifier's resolved rows (accessor, alias, facade, XOperations(project)) and adds an AST pass for raw LCM names (server/exclusive_access.py).
    • A second AST layer (_facade_generic_matches) catches Create/Delete on an untyped <x>.WritingSystems / <x>.CustomFields receiver, including a local alias, a chained rebind, a module-level alias, or a for loop over a literal list. The certifier leaves these as unresolved_receiver rows. Values derived from the facade (p.WritingSystems.GetAll()) are not gated. Not followed yet: tuple unpacking and with ... as bindings.
    • The error-code count goes from 47 to 48 (main's unknown_method, [WM] Nonexistent method (ParserOperations.TryWord) classified as mutating -> unprotected_writes loop; return unknown_method + did-you-mean #306, took it to 47 first). The change is additive under tool-responses/1.x, with a golden fixture.
  • Conditional WritingSystems.Ensure() (FR-002b). An Ensure() is checked first instead of refused outright.
    • The project's active lists (CurVernWss / CurAnalysisWss) are streamed from the .fwdata: no project open, about 150 ms, then cached on (size, mtime).
    • An already-active literal tag runs. A tag that would be added is refused up front with the reason. A non-literal tag, or an unreadable file, is left to flexicon's new peer schema guard (feat(ws): peer schema guard -- refuse WS schema writes from a shared-mode peer flexicon#601), which raises before writing.
    • That runtime refusal is mapped to the same code with stage: runtime. The guard is also on for every write-enabled run on a gated verdict, as a backstop.
    • This fails closed without flexicon#601: Ensure() is refused as before.
  • validate_only gains project_lock.exclusive_access = {required, operations, blocking, ...} and makes the same decision as the real run.
  • Messages:
    • an open_shared project-open failure no longer says "Close FieldWorks and retry";
    • the open_shared advisory points at the refusal;
    • the gate has its own assistance hint.
  • Docs:
    • the remaining undo claims are removed (workflow-summary.md, the style guide, flextools_start's description);
    • SHARED-MODE.md documents the refusal and the conditional Ensure(), how to see an MCP change in FLEx (navigate away and back; F5 alone is not enough), why FLEx's Undo cannot reverse an MCP write, and that the save-conflict hazard is fixed in flexicon 4.6.0.

Merge with main

origin/main is merged in (merge commit, no rebase): #306/#338/#339/#342 unknown_method, #315/#336 project_locked next_steps, #341 archive move of shared-mode-access, and dependency bumps. Conflicts were resolved by keeping both sides:

  • docs/TOOL-CONTRACT.md: main's project_locked row with next_steps, next to requires_exclusive_access.

  • execution.py _diagnose_project_open_error: main's own-worker holder check (project_locked by a python PID the model cannot end; verify whether holder is the idle parse worker #315) runs first, then the FR-011 open_shared hint.

  • response_models.py and the two count tests: both codes listed, and the count is 48 everywhere (TOOL-CONTRACT, response_models, tests, CHANGELOG).

  • docs/SHARED-MODE.md: the branch's expanded WS/CF table, Ensure() section and corrected LCM citations. Evidence paths now point to specs/_archive/shared-mode-access/, and so does exclusive_access._WS_LIVE_EVIDENCE.

  • "Close FieldWorks" message class: reviews/pattern-audit-close-fieldworks.md. Three sites fixed; three kept, with the reason for each.

  • Detector shape (receiver/name matching, role confusion): reviews/sweep-detector.md. No sibling found in the detector. Pre-existing write-gate siblings in validators.py are logged as filing candidates.

  • Untyped-facade receivers: nothing else in src/ matches certifier rows by name with a generic-name exclusion. unresolved_receiver rows come only from validators.py:6993/7013, and _raw_matches already uses the whole receiver chain.

  • After the merge: the stale error-code counts and specs/shared-mode-access/ paths were swept. Remaining hits are intentional (they point to the predecessor's AS-BUILT, or are historical task text).

Tests

  • tests/test_exclusive_access_detect.py (78, including 10 untyped-facade cases), tests/test_exclusive_access_gate.py (43) and tests/test_docs_no_undo_claims.py.
  • Contract, diagnosis and advisory tests updated.
  • Full offline suite after the merge: 5065 passed, 4 skipped. Goldens up to date; validate_integrity.py server clean; pre-commit clean.

Live verification (Sena 3, FieldWorks PID 27984, sharing on): specs/exclusive-access-gate/evidence/live-gate.md

  • V1: Ensure refused; .ldml unchanged; no backup; FLEx responsive
  • V2: validate_only gives blocking: true
  • V4: an ordinary write proceeds with the new advisory
  • V5: sync-at-open made no difference to a read-back (it saw the write because idle FLEx had already flushed), so it is not shipped
  • V8a-d: conditional Ensure allowed / refused up front / refused at run time / refused without the guard; WS lists unchanged
  • V6: after reopening FLEx the MCP gloss is present and Undo is empty (in-session navigate-away and Undo-omits-peer-write not re-tested; see evidence)
  • V7: a peer-added custom-field definition never reached the master, other peers or disk (silently lost, confirmed live)
  • V3: no-op Ensure with FLEx closed passes the gate and writes nothing; FLEx reopens cleanly
  • Cleanup: zzExclTest entry deleted; qaa-x-zzexcl residue removed

Before merge

🤖 Generated with Claude Code

MattGyverLee and others added 14 commits September 30, 2026 21:04
…ess (#93)

Carries forward what #93 left unbuilt: the requires_exclusive_access gate
for writing-system and custom-field changes while FLEx holds the project,
a valid live test for the custom-field hazard, the remaining undo and
shared-mode doc corrections, SyncForeignChanges for write-enabled runs, and
the unfiled upstream issues. Programmatic undo is out of scope.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…, quickstart

Plan for the requires_exclusive_access gate (successor to #93). LCM source
settles the FLEx-undo question: reconciled peer writes land on the
non-undoable stack, so the spec's Section 2 is amended from "unknown". The
MCP runs undoable=True under flexicon 4.11, so read-back sync uses
SaveChanges(), and read-only sessions cannot sync. The domain gate and the
plan QC gate (one blocking count site, now scheduled) are recorded in
reviews/.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…the project

Phases 1-4 of specs/exclusive-access-gate (MVP: US1 + US2).

A write-enabled run_module whose script changes writing systems or custom
fields is refused with the new code requires_exclusive_access when the
access probe says FieldWorks holds the project in shared mode (open_shared)
or cannot tell (unknown). The refusal comes before the confirmation gate,
the backup and any subprocess. open_exclusive / held_by_other keep their
project_locked refusal.

- server/exclusive_access.py: EXCLUSIVE_ONLY_OPERATIONS (one table for the
  detector, the refusal and the docs) and detect_exclusive_only_operations
  (certifier rows for wrapper calls + one AST pass for raw LCM names,
  receiver-scoped generic methods and schema-property assignments).
  Untyped-receiver rows match wrapper names by name, except Create/Delete,
  which are mutating on ~50 other Operations classes and on every raw LCM
  factory.
- handle_run_module: detect on the final (post-auto-fix) code, force the
  probe on a match, refuse before confirmation.
- validate_only: project_lock.exclusive_access = {required, operations,
  blocking}; blocking is true on unknown (the real run refuses it, FR-009),
  null only when the probe raised.
- Contract: RequiresExclusiveAccessDetail, union 46 -> 47, golden fixture,
  TOOL-CONTRACT.md row and counts (stale "44 codes" fixed).
- US2: specific assistance hint; open_shared open failures no longer say
  "Close FieldWorks and retry"; the open_shared advisory points at the gate.

Pattern audit ("close FieldWorks" on a shared project), full table in
specs/exclusive-access-gate/reviews/pattern-audit-close-fieldworks.md:
- execution.py _diagnose_project_open_error generic hint: FIXED for
  open_shared; kept for free / failed probe.
- write_ladder.py refusal fallback: reworded (open_exclusive/held_by_other
  only; no longer assumes FieldWorks is the holder).
- write_ladder.py open_shared advisory: FIXED (points at the gate).
- project_discovery.py:398: kept, branch runs only with sharing OFF.
- project_discovery.py:419: comment only.
- teardown_recovery.py:180: kept, abandoned machine-wide WS-store mutex,
  unrelated to the project lock.

Tests: tests/test_exclusive_access_detect.py (49), test_exclusive_access_gate.py
(23), contract/diagnosis/advisory updates. Full offline suite: 4774 passed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…shipped

Phase 5 of specs/exclusive-access-gate (US4).

- workflow-summary.md: Stage 6 is "Inspect" (logs, session history); the
  undo tool, undo stack and "undo entry recorded" claims are gone; the
  pre-write backup is named as the safety net.
- FLEXTOOLS-STYLE-GUIDE.md: the "undo_last_operation can reverse a write"
  layer is replaced by "there is no undo", linking RECOVERY.md.
- SHARED-MODE.md: "Close FLEx for these" says the server refuses these with
  requires_exclusive_access and gives the close / re-submit / reopen steps;
  rows name every wrapper method in EXCLUSIVE_ONLY_OPERATIONS; new "Seeing an
  MCP change in FLEx" (navigate away and back, F5 alone is not enough, #96);
  "Undo" gains the four facts (no MCP undo, FLEx opens with an empty undo
  history, FLEx records peer writes as non-undoable, programmatic undo not
  built) with LCM citations.
- tool_definitions.py (flextools_start): no longer claims writes land in
  FLEx's Ctrl+Z menu.
- tests/test_docs_no_undo_claims.py: the tool may be named only as removed;
  no "can reverse a write"; SHARED-MODE.md phrases; one row per category.
  docs/archive/ is excluded as history.
- WS evidence path corrected to specs/shared-mode-access/ (not archived).

validate_integrity.py server: clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Offline parts of phases 7-9 of specs/exclusive-access-gate.

- evidence/live_cf_peer.py (T029): Class B harness. dry / add / check modes;
  asserts Sena 3; peer-adds the zzExclTest custom-field definition via
  FieldDescription.UpdateCustomField inside a non-undoable task, writes no
  data. Not yet run (needs FLEx open; T030 is needs-human).
- issues/filing-ledger.md (T032-T034): the four flexicon hazards are already
  fixed (cb1d355 #285, 287bb20 #233/#234, b3a5bb9 #236); the CreateField
  CheckNotProcessingDataChanges comment is still new. Archived drafts
  triaged: 8 still new (flexicon-5 retargeted to FlexToolsMCP), 5 fixed or
  duplicate. Nothing filed; T035 waits for the maintainer.
- SHARED-MODE.md (T037): the save-conflict hazard is described as fixed in
  flexicon 4.6.0 (HeadlessLcmUI default), replacing "upstream issue not
  yet filed".
- reviews/sweep-detector.md (T039): no confirmed sibling in the detector;
  pre-existing write-gate siblings in validators.py logged as filing
  candidates.
- CHANGELOG.md (T038): Tool contract and Other entries.

Full offline gate (T040): 4829 passed; goldens up to date; integrity clean;
pre-commit clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…lowed error

after_implement lex-qc gate (reviews/implement-qc.md, 84/100):
- N1: exclusive_access.REFUSING_VERDICTS -> GATED_VERDICTS; it had the
  opposite meaning of write_ladder.REFUSING_VERDICTS on adjacent lines.
- N6: validate_only's probe-unavailable exclusive_access branch logs at
  debug instead of passing silently.
B2 (pattern audit in the b52a7de body) confirmed. B1 (live evidence) stays
open: needs FieldWorks open on Sena 3 and the maintainer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… run time

FR-002b amendment (maintainer-approved 2026-10-01). WritingSystems.Ensure()
writes only when the tag is not already active in the requested category,
so refusing every Ensure() made users close FieldWorks for no-ops.

Two layers:
1. Before the run, the project's ACTIVE lists (CurVernWss / CurAnalysisWss)
   are streamed from the .fwdata -- pure filesystem, no project open,
   156 ms on Sena 3's 53 MB file (an earlier LCM-subprocess snapshot took
   ~4 s and was replaced at the maintainer's suggestion). The .ldml store
   is deliberately not used: store-present tags can be inactive.
   plan_conditional: literal + active -> run; literal + would add ->
   refuse now (stage: preflight) with the reason; non-literal or unreadable
   file -> deferred to layer 2. Any unconditional match, or no guard in the
   installed flexicon, refuses as before.
2. During the run, flexicon's peer schema guard (capability
   "peer-schema-guard", flexicon branch feat/peer-schema-guard 076a239)
   raises FP_ExclusiveAccessRequiredError before writing; mapped to
   requires_exclusive_access with stage: runtime. The runner's
   PEER_SCHEMA_GUARD line (off / on / required) is patched after the probe;
   "required" stops the run before user code if the guard is missing, so a
   server/subprocess flexicon mismatch fails closed. The guard is "on" for
   every write-enabled run on a gated verdict as a backstop.

Also: ws.ensure table row (conditional=True); detail model gains `stage`,
match model gains `conditional`; success result carries exclusive_access;
warning when Ensure() follows other writes; validate_only makes the same
plan from the same file read (blocking / conditional / deferred_to_runtime
/ notes). Docs: SHARED-MODE "Ensure() is checked first", TOOL-CONTRACT
row, spec FR-002b, data-model, contract, CHANGELOG. Golden regenerated.

Earlier work in this commit's tree (evidence): live-gate.md V1, V2, V4, V5
(sync-at-open NO-SHIP), V7 dry, V8a-d, plus the live driver and snippets.

Tests: detector 68, gate 39; full offline suite 4865 passed; goldens up to
date; integrity clean; pre-commit clean.

Live V8 (Sena 3, FieldWorks PID 27984, sharing on): a) active Ensure ran,
created=False; b) literal new tag refused preflight with reason; c) the
same tag via a variable refused at run time, nothing written; d) released
flexicon (no guard) refused preflight. WS lists and .ldml mtimes unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…mtime)

The conditional-Ensure() read runs only for write-enabled runs whose only
exclusive-only calls are Ensure() while FieldWorks holds the project, but
it no longer re-reads an unchanged file: _ACTIVE_WS_CACHE keys each
.fwdata path on (size, mtime_ns), so a hit costs one stat(). Any FLEx save
or MCP write changes the file and forces a fresh read, so a writing system
added since the last read is never refused from a stale entry (the case a
once-per-session cache would get wrong). Unsaved FLEx state is invisible
either way; the runtime peer schema guard covers it. Failed reads are not
cached.

Tests: hit (one read for two calls), changed file re-read, failed read not
cached, missing .fwdata. Live V8a recheck on Sena 3: allowed, guard on.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…cleanup

- flexicon main pulled to 04786b0 (#601 merged): MCP offline suite 4869
  passed. flexicon offline: the guard tests pass; 47 pre-existing failures
  (575/577/581/docstring ratchet) fail identically at ad4a1c2, main before
  the merge.
- Live re-run happened with FieldWorks closed (verdict free), so the gate
  correctly did not apply and V8b created qaa-x-zzexcl in Sena 3. It was
  removed with WritingSystems.Delete (op-113850747); WS lists verified. An
  .ldml and an idchangelog <Add> remain (ledger flexicon-6), pending the
  maintainer. V8a doubled as the corrected V3 (FLEx closed, active tag:
  passes the gate, writes nothing).
- live_gate_driver.py gains `--expect <verdict>` so a step refuses to run in
  the wrong project state. cleanup_zzexcl_ws.py added.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
V3: Ensure('en', analysis) with FLEx closed passed the gate and wrote
nothing; FLEx reopened cleanly without qaa-x-zzexcl. V6: after reopen the
zzExclTest gloss zzgloss-v4 (MCP peer write) is present and Undo is empty.
In-session navigate-away/F5 and peer-write-not-undoable were not re-tested
this round (FLEx had been reopened); docs keep citing #96 and research R4.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Per the maintainer: a fresh FLEx start always shows the current data; the
navigate-away-and-back refresh (#96) applies only to a change made while
FLEx was open.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…28 note

V7 (Sena 3 held by FieldWorks PID 6468, sharing on; Claude-Swahili open in
another FLEx and untouched): a peer added the zzExclTest custom-field
definition (FieldDescription.UpdateCustomField in a non-undoable task, no
data) and committed. A fresh peer did not see it, the master's Custom
Fields dialog did not list it, the .fwdata was untouched, and after FLEx
closed it was still absent: silently lost, confirmed live (stronger than
R9's "visible, then gone"). SHARED-MODE.md and the cf.* evidence updated;
live_cf_peer.py: .NET imports moved after FLExInitialize.

Cleanup with Sena 3 closed: qaa-x-zzexcl.ldml deleted and its one
idchangelog <Add> removed (maintainer-approved; encoding/CRLF kept; copies
in the session scratchpad); zzExclTest entry deleted (op-092650017,
verified absent).

T028: shared_mode_read_back notes (runner advisory + runtime primer)
replace "untested" with the V5 result, read correctly: the read-back saw
the write only because FLEx was idle and the master had already flushed the
.fwdata; it is not a guarantee, and SaveChanges() in the reader made no
difference. The V5 evidence's "reaches disk at commit time" is corrected.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…cleanup

No zzExclTest custom field or entry; writing systems seh, seh-fonipa-x-etic
/ en, pt with no qaa-x-zzexcl. All live steps V1-V8 complete.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 2, 2026
MattGyverLee and others added 2 commits October 2, 2026 10:04
flexicon#602-#609 and FlexToolsMCP#347-#352, filed 2026-10-02 with the
maintainer's OK; ledger rows carry the issue numbers. #350 (unguarded
coll.Add through a loop over LCM collections passes the write gate) was
reproduced offline before filing; #352 (compound modifyAllowed guard
rejected) and flexicon#609 (47 pre-existing offline failures on main) are
new rows. The V7 upstream row is not applicable (V7 ran).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brings in unknown_method (#306/#338/#339/#342), project_locked holder and
next_steps (#315/#336), and the shared-mode-access archive move (#341).

Conflict resolution keeps both sides: the requires_exclusive_access row and
model sit next to main's project_locked next_steps and unknown_method; the
open_shared project-open hint (FR-011) now runs after main's own-worker
holder check. The error-code count moves to 48 everywhere it is stated
(TOOL-CONTRACT.md, response_models.py, both count tests, CHANGELOG).
Evidence paths that pointed at specs/shared-mode-access/evidence/ now
point at specs/_archive/shared-mode-access/evidence/.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
MattGyverLee and others added 3 commits October 2, 2026 12:09
…e (T041)

Records phase 10 and the origin/main merge in .spec-context.json, the
48-code count, and that T048 (flexicon release) is still open:
flexicon#601 is merged but unreleased, so the pyflexicon floor and the
indexes stay as they are.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ms receiver

A writing-system Create or Delete called through a helper's project
parameter (p.WritingSystems.Delete), or through a local alias of the
facade attribute (w = p.WritingSystems; for w in [project.WritingSystems]),
left only an unresolved_receiver row, and the generic-name exclusion
dropped it. Under open_shared/unknown the run went ahead and could crash
the FieldWorks holding the project. An AST layer now matches the generic
names when the receiver is the facade attribute or bound from it;
unrelated receivers and GetAll() results still do not match.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…h, T041 push status

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brings in #354 (write-gate fail-opens), #353 (surface recipes), #355 (docs), #356 (preflight retry loops).

Conflict resolution keeps both sides: unknown_import (#305) and requires_exclusive_access sit side by side and the error-code count moves to 49 everywhere (response_models, both count tests, TOOL-CONTRACT, CHANGELOG). execution.py keeps main's #334 assistance-log ordering with the gate's requires_exclusive_access key. test_issue55 ladder test stubs the exclusive-access detector so it still exercises Rung 3 (gate covered in test_exclusive_access_gate.py). Full offline suite: 5273 passed, 4 skipped.
@MattGyverLee
MattGyverLee marked this pull request as ready for review October 2, 2026 18:42
@MattGyverLee
MattGyverLee merged commit 8a3339d into main Oct 2, 2026
2 checks passed
@MattGyverLee
MattGyverLee deleted the feat/exclusive-access-gate branch October 2, 2026 19:03
MattGyverLee added a commit that referenced this pull request Oct 2, 2026
…360)

* fix(exclusive-access): review findings 2-4 on the gate

- ensure_call_args: *args/**kwargs unpacking -> unresolved (None), so the
  call defers to the runtime peer guard instead of being judged against the
  wrong category's active list with a defaulted is_vernacular=True.
- _wrapper_matches: a conditional Ensure() on an unresolved_receiver row no
  longer matches by method name alone; it needs receiver evidence (resolved
  WritingSystemOperations class or a .WritingSystems facade, the latter
  matched by the AST facade pass).
- Facade aliases are swept in source order: rebinding a name to a
  non-facade value drops the alias, and function/lambda parameters shadow
  inherited module aliases.

* fix(exclusive-access): runtime refusal carries the documented envelope

Finding 1 on the #343 review: a runtime peer-guard refusal
(FP_ExclusiveAccessRequiredError / PeerSchemaGuardUnavailable) previously
produced error_code + stage: runtime but no operations, verdict, flat
message, or nested error object, so the published
requires_exclusive_access envelope could not be validated from it.

- _diagnose_exclusive_access_runtime_error now takes the conditional
  matches the preflight plan let through plus the probe verdict/holder,
  and the call site passes them from _exclusive_matches/_decision/_access.
  The message names the refused operations, build_refusal-style.
- The diagnosis promotion builds the dual envelope mirroring
  error_response(): flat message + nested error {code, message, ...detail},
  with guidance/verdict/holder/operations/remedy/stage at top level. The
  raw .NET text moves to raw_error; messages and backup status untouched.
- Downstream string assumptions hardened: _error_message_text() coerces
  the nested error object for _log_operation_failure and the pattern
  tracker's record_operation (the latter crashed the handler with
  TypeError when a tracker was initialized -- caught only by the
  full-suite run, not by the gate tests alone).

Finding 2-4 (unresolved Ensure needs receiver evidence; **kwargs/*args
unresolved; facade aliases respect rebinding/shadowing) are in the
preceding commit.

---------

Co-authored-by: Muse <muse@local>
MattGyverLee added a commit that referenced this pull request Oct 5, 2026
All 48 tasks complete: the gate merged as 8a3339d (PR #343) with review
fixes in 6673a65 (PR #360), released in 2.15.0 (#365). T048 (needs-human)
closed: pyflexicon 4.12.0 ships the peer-schema-guard capability and PR
#364 regenerated the indexes against it with the floor raised.

Writes specs/exclusive-access-gate/AS-BUILT.md, git mvs the full docs
(spec, plan, tasks, research, data-model, contracts, checklists,
quickstart, reviews, evidence, issues/filing-ledger) to
specs/_archive/exclusive-access-gate/, and repoints the live references
(docs/SHARED-MODE.md, CHANGELOG.md, CLAUDE.md SPECKIT section, code
comments, test docstrings) at the archive or the live one-pager.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant