Files
dc/AGENTS.md
T
dc af28a79bd5
Build / Build-and-ng-test (pull_request) Successful in 6m26s
Lighthouse Checks / lighthouse (pull_request) Successful in 20m36s
Build / Build-and-test-development (pull_request) Successful in 35m20s
fix(lint): make noUndeclaredMacros an error, and fix what it found
Rung 2 of the #376 ratchet: `sas/.sasjslint` promotes noUndeclaredMacros to
"error". Its 25 findings on release/7.22 are cleared, and two of them were
real faults rather than missing prose.

WHAT WAS BROKEN

Comparing the compiled program against the macros it calls (compile, then
list every project macro each generated service calls but does not define)
found three services calling macros that were absent from their own
compiled program:

- services/validations/tables_all.sas calls %mf_getuniquename() on its
  third line and never declared it.
- services/validations/mpe_tables.dsn.sas calls %mf_getuniquename() and
  %dc_assignlib() and declared neither.
- services/hooks/mpe_tables_postedit.sas calls %dc_assignlib(), which is
  supplied by the service that includes the hook (see below).

The two validation services are configured as rule_value entries in
MPE_TABLES (mpe_makedata.sas and the v3.12/v3.13 migrations), so the calls
run when a user reviews or filters a library - each would have raised
"Apparent invocation of macro ... not resolved". Both now declare what
they call, and the definitions are present in the compiled program.

WHAT CHANGED

- 14 files declare the macros they call but had been relying on another
  declaration to bring in: mf_getquotedstr, mf_nobs, mf_getuser (twice),
  mf_getapploc, mf_getattrn, mp_abort (six services), mf_getuniquename
  (twice), dc_assignlib, bitemporal_closeouts. These were defined in the
  compiled program already; the declarations make the dependency visible
  and the two validation services correct.
- 6 hook scripts get a file-local @sasjslint {"ignoredMacros": [...]}
  (mp_abort, plus dc_assignlib in mpe_tables_postedit) instead of a macro
  list. Hooks are included at run time by the service that owns them, so
  the macros are already in that session, and these files are copied and
  edited by end users - they should not carry a declaration section to
  maintain. Recorded in AGENTS.md.
- bitemporal_dataloader.test.2.sas loses its test 30 block: it is gated
  with `and 0` ("skip until have a db") and its only setup line calls
  %assign_direct_lib, a macro that exists nowhere in this project or in
  @sasjs/core. Enabling it would fail on the first line.

VERIFICATION

- `npm run lint:sas`: 18 warnings, 0 errors, exit 0.
- `sasjs compile`: succeeds; the called-but-undefined list drops from
  three services to the one hook exception; the only definition-count
  changes are additions.
- 18 warnings remain for the next rungs: noBannedStrings (13), macro name
  length (2) and case (2), indentationMultiple (1).
2026-11-01 01:19:55 +00:00

5.6 KiB

Agent Instructions

Read CONTEXT.md at the repo root first - it is the domain glossary and orientation for Data Controller (roles, load types, MPE control tables, validations, security). Use its vocabulary in your output.

Data Controller spans three sibling repos (usually checked out side by side):

  • dc (this repo) - the product source (Angular client + SAS backend).
  • docs.datacontroller.io - the user-facing product documentation (MkDocs). User-facing behaviour is documented there; the .agents/docs/ deep-dives here link out to it.
  • datacontroller.io - the marketing site, blog and feed (Gatsby).

Git

Do NOT auto-commit. Never run git commit (or git push) unless the user explicitly asks. Leave changes in the working tree for the user to review and commit themselves.

CHANGELOG and versioning

Do NOT edit CHANGELOG.md by hand, and do NOT bump the version in package.json. The release pipeline (semantic-release, in .gitea/workflows/release.yaml) generates both automatically from Conventional Commit messages on every push to main. Manual entries collide with the pipeline's release commit and are overwritten anyway. Control the changelog through your commit messages instead. See .agents/docs/releases-and-changelog.md.

Markdown files

Do NOT hard-wrap Markdown files (no fixed-width line wrapping / carriage returns inside paragraphs). Each paragraph, list item, and heading should be a single logical line, regardless of length. Let the editor/viewer soft-wrap. This includes AGENTS.md itself.

This is different from .sas files, where a maximum line length applies. The no-wrap rule applies to all *.md files in this repo (docs, READMEs, etc.).

Rationale: hard-wrapped prose produces noisy diffs when sentences are edited and reflowed, and Markdown renderers already handle wrapping.

Linting (required before "done")

Never consider a change complete until the relevant linters pass on the files you touched — do not rely on the user's pre-commit hooks to catch it:

  • Client (TypeScript/HTML/etc.): run npm run lint:check from the client/ directory (prettier). Fix any failures with npm run lint:fix.
  • SAS: after creating or modifying any .sas files, run npm run lint:sas from the sas/ directory. That script runs the @sasjs/cli version pinned in sas/package.json, so it matches CI; a global or npx sasjs may resolve a different @sasjs/lint and report a different rule set. Rules are promoted to error in sas/.sasjslint one at a time as their findings reach zero, and only errors change the exit code - so the whole project must stay clean of the promoted rules, not just the files you touched. Warnings under rules not yet promoted are the remaining backlog; do not add to them in files you touch, and do not clear other files' findings in the same change unless that rule is the point of the change. sas/.sasjslint carries ignoredMacros: ["webout", "mpeinit", "mpeterm"]. webout is the linter's own default - sasjs compile injects %webout into every service - and an explicit list replaces that default, so it has to be restated there. mpeinit and mpeterm are listed because every service is compiled with a serviceInit program (sasjs/utils/serviceinitsas9|viya|server.sas) that declares both macros in its own header, so a service body may call them without declaring them. Add a macro to that list only when it reaches a file the same way - injected by the compiler, through serviceInit, testSetUp or another implicit provider - never to silence a genuinely missing declaration. noBannedStrings flags the literal words error and warning anywhere in a file, comments included. Reword prose; where the text has to stay (a real %put, a doxygen @warning tag), keep the log output identical with %put %str(WARN)ING: rather than a per-file override, and use @sasjslint {"noBannedStrings": false} only when the word itself is the content. Hook scripts (sasjs/services/hooks/*.sas) run inside the service that includes them, so the macros they call come from that service's session. They carry a file-local @sasjslint {"ignoredMacros": [...]} rather than a macro list of their own, because end users copy and edit these scripts and should not have to maintain a declaration section.

No external assets

Data Controller must run entirely locally (offline / on-prem, no internet access). The built product must never fetch assets from remote servers — no external fonts, images, scripts, stylesheets, CDN links, or remote URLs in meta tags (e.g. og:image, og:url, itemprop="image"). All assets must be bundled and served locally.

The .agents folder

Agent-related content lives in .agents/: technical/agent-facing documentation goes in .agents/docs/ (not docs/), and skills in .agents/skills/. When writing explanatory or technical docs about the codebase, put them in .agents/docs/.

Skills installed via npx skills add are tracked in skills-lock.json and live under .agents/skills/. Repo-local custom skills (e.g. dc-sas for Data Controller SAS development, dc-estate-testing for estate deploys and test runs, handsontable, hyperformula) are kept here too. Avoid editing npx-managed skill files directly — use npx skills update instead.

Code comments and test names

Never reference items that are not active parts of the repository — no "the original bug", "regression from this fix", "this session/PR/commit", or similar ephemeral context. Comments and test names must be self-contained: describe the behaviour being asserted, not the history of how it was discovered. The one exception is a literal link to a ticket/issue tracker.